package com.easytouch.core;

import android.os.SystemClock;
import android.util.Pair;

import com.easytouch.core.encryption.MD5;
import com.easytouch.core.listener.OnCoinReleaseLeverListener;
import com.easytouch.core.listener.OnDoorStateChangedListener;
import com.easytouch.core.listener.OnFreshColumnListener;
import com.easytouch.core.listener.OnGameButtonPressedListener;
import com.easytouch.core.listener.OnGoodsSelectedListener;
import com.easytouch.core.listener.OnOutGoodsListener;
import com.easytouch.core.listener.OnReceiveMoneyChangedListener;
import com.easytouch.core.model.ColumnModel;
import com.easytouch.core.model.GoodsModel;
import com.easytouch.core.model.WarrantyModel;
import com.easytouch.core.utils.FileTools;
import com.easytouch.core.utils.Log;
import com.easytouch.core.utils.Tools;

import org.json.JSONException;
import org.json.JSONObject;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.security.InvalidParameterException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.concurrent.ConcurrentHashMap;

import android_serialport_api.SerialPort;

//import com.easytouch.SerialPort;


/**
 * 与串口的驱动类
 * Created by gulin on 2017/7/17.
 */

public class DriverThread extends Thread {

    // 主控板返回数据的总长度
    public static final int MAX_ROAD_NUM = 48;
    private static final int T76_DATA_LEN = 16;
    private static final int T78_DATA_LEN = 25;
    private static final int T79_DATA_LEN = 60;
    private static final int T7A_DATA_LEN = 22;
    private static final int T7B_DATA_LEN = 81;
    private static final int T7C_DATA_LEN = 34;
    private static final int T7D_DATA_LEN = 15;
    private static final int T7E_DATA_LEN = MAX_ROAD_NUM + 6;
    private static final int T7F_DATA_LEN = MAX_ROAD_NUM * 2 + 6;
    private static final int T75_DATA_LEN = 151;
    private static final int T71_DATA_LEN = 43;
    private static final int T72_DATA_LEN = 72;
    private static final int T73_DATA_LEN = 92;

    public static final int BOX_YL = 11; //饮料机
    public static final int COLUMN_DISABLED = 238; //禁用货道编号

    public static final String OUT_GOODS_CASH = "01"; //现金
    public final static String OUT_GOODS_TRANSCARD = "02"; //一卡通
    public final static String OUT_GOODS_UNIONPAY = "11"; //银联
    public final static String OUT_GOODS_FASTBUY = "20"; //一键购物
    public final static String OUT_GOODS_TIHUO = "21"; //提货
    public final static String OUT_GOODS_POINT = "31"; //积分
    public final static String OUT_GOODS_ALIPAY = "41"; //支付宝
    public final static String OUT_GOODS_WEIXINPAY = "42"; //微信
    public final static String OUT_GOODS_GAME = "43"; //游戏
    public final static String OUT_GOODS_BAIPAY = "44"; //百付宝
    public final static String OUT_GOODS_CCB = "45"; //建行
    public final static String OUT_GOODS_TRAVELCARD = "46"; //旅游卡
    public final static String OUT_GOODS_ALIFACE = "47"; //阿里人脸支付
    public final static String OUT_GOODS_YQYKT = "48"; //园区一卡通
    public final static String OUT_GOODS_SUNING = "49"; //苏宁易付宝
    public final static String OUT_GOODS_BESTPAY = "4A"; //翼支付
    public final static String OUT_GOODS_UNIONPAY_QR = "4B"; //银联二维码支付
    public final static String OUT_GOODS_CLOUE_QR = "54";//云码支付
    public final static String OUT_GOODS_AB = "4C"; //农行支付
    public final static String OUT_GOODS_BJYKT = "4D"; //北京交通一卡通SDK
    public final static String OUT_GOODS_LF = "4E"; //带JAR包的银商POS机（朗方）
    public final static String OUT_GOODS_UPCARD = "4F";//up卡支付
    public final static String OUT_GOODS_ROAD_TEST = "50";//料道出货

    private Log mlogger; //日志

    private SerialPort mSerialPort;
    private OutputStream mOutputStream;
    private InputStream mInputStream;

    private volatile boolean isStopped = true;

//    private Context mContext;

    private static final int OUT_GOODS_COUNT = 3;
    private int outGoodsCount = OUT_GOODS_COUNT; //出货重试次数
    private boolean isFristCmd = true;
    private String mTraceNo = ""; //7C指令的序列号,防止多次发送7C
    private long mSelectTime = 0; //上次的按键选货时间
    int mBox = 0; //上次按键选货的货柜
    int mColumn = 0; //上次按键选货的货道
    int mGoods = 0; //上次按键选货的商品编号
    private byte mChargeState = 0; //充电状态
    private int mCmd78Count = 0; //78指令收到次数

    /**
     * 各种通知
     */
//    //出货通知
//    public static final String INTENT_OUT_GOODS = "com.easytouch.core.DriverThread.outgoods";
//    public static final String EXTRA_OUT_GOODS_RESULT = "com.easytouch.core.DriverThread.outgoods.result";
//    public static final String EXTRA_OUT_GOODS_TRACE = "com.easytouch.core.DriverThread.outgoods.trace";
//    public static final String EXTRA_OUT_GOODS_TYPE = "com.easytouch.core.DriverThread.outgoods.type";
//    //刷新通知
//    public static final String INTENT_REFRESH_ROAD = "com.easytouch.core.DriverThread.refresh";
//    //按键选货通知
//    public static final String INTENT_SELECT_GOODS = "com.easytouch.core.DriverThread.selectGoods";
//    public static final String EXTRA_SELECT_GOODS_BOX = "com.easytouch.core.DriverThread.selectGoods.box";
//    public static final String EXTRA_SELECT_GOODS_COLUMN = "com.easytouch.core.DriverThread.selectGoods.column";
//    public static final String EXTRA_SELECT_GOODS_ID = "com.easytouch.core.DriverThread.selectGoods.id";
//    public static final String EXTRA_SELECT_GOODS_PRICE = "com.easytouch.core.DriverThread.selectGoods.price";
//    //门状态变化通知
//    public static final String INTENT_DOOR_STATE = "com.easytouch.core.DriverThread.doorstate";
//    //投币金额变化通知
//    public static final String INTENT_RECEIVE_MONEY = "com.easytouch.core.DriverThread.receivemoney";

    private OnReceiveMoneyChangedListener mOnReceiveMoneyChangedListener;
    private OnGoodsSelectedListener mOnGoodsSelectedListener;
    private OnFreshColumnListener mOnFreshColumnListener;
    private OnDoorStateChangedListener mOnDoorStateChangedListener;
    private OnGameButtonPressedListener mOnGameButtonPressedListener;
    private OnCoinReleaseLeverListener mOnCoinReleaseLeverListener;
    private OnOutGoodsListener mOnOutGoodsListener;

    //开关门状态
    public static final int DOOR_UNKNOWN = -1;
    public static final int DOOR_OPEN = 1;
    public static final int DOOR_CLOSE = 0;
    public volatile int doorState = DOOR_UNKNOWN;

    //取物口状态
    public static final int FETCH_GOODS_WAIT = 1;
    public static final int FETCH_GOODS_FINISH = 0;
    public volatile int fetchGoodsState = FETCH_GOODS_FINISH;
    private long mLastExitDoorOpenTime = 0;
    private long mLastFetchGoodsFinishTime = 0;

    //主动指令执行结果
    public static final int CMD_NONE = -1;
    public static final int CMD_ACK = 0;
    public static final int CMD_NAK = 1;
    public volatile int cmdResult = CMD_NONE;

    //收到投入的金额
    public int totalMoney = 0; //总金额
    public int paperMoney = 0; //纸币
    public int coinMoney = 0; //硬币

    public boolean isStartNormal = true; //是否是正常启动
    public int softwareType = 0; //0：横屏，1：竖屏，2：小屏

    private HashMap<Integer, String> mDataMap7E = new HashMap<>();
    private HashMap<Integer, String> mDataMap7F = new HashMap<Integer, String>();
    private HashMap<Integer, String> mDataMap7A = new HashMap<Integer, String>();
    private HashMap<Integer, String> mDataMap72 = new HashMap<Integer, String>();
    private String mData7D;

    private int[] mBox11Stock = new int[MAX_ROAD_NUM]; //饮料机库存
    public boolean mBoxLock = false; //光感检测失败是否锁定货道(饮料机出货结果是1，食品机是3时)
    public boolean mBox11Retry = false; //饮料机未检测到出货是否重试

    private HashMap<String, String> mBox11MessageMap = null;

    public ConcurrentHashMap<Integer, ColumnModel[]> mRoadInfo = new ConcurrentHashMap<Integer, ColumnModel[]>();

    private ConcurrentHashMap<Integer, String[]> mWarrantyInfo = new ConcurrentHashMap<>();

    private LinkedList<byte[]> mQueue = new LinkedList<byte[]>(); // 主动指令队列

    public synchronized void appendQueueData(byte[] data) {
        mQueue.offer(data);
    }

    public synchronized byte[] getQueueData() {
        return mQueue.poll();
    }

    //流水号和订单号的映射
    private HashMap<String, String> mOrderMap = new HashMap<String, String>();
    private long mLastAddOrderIdTime = 0;

    private ArrayList<GoodsModel> mDisabledGoodsList = new ArrayList<>(); //商品置灰列表

    /**
     * 添加流水号和订单号的映射
     *
     * @param pTrace   流水号
     * @param pOrderId 订单号
     */
    private synchronized void addOrderId(final String pTrace, final String pOrderId) {
        long currentTime = System.currentTimeMillis();
        if (currentTime - mLastAddOrderIdTime > 1000 * 60 * 5) //5分钟之后删除掉所有记录
        {
            if (mOrderMap.size() > 0) {
                mOrderMap.clear();
            }
        }
        mOrderMap.put(pTrace, pOrderId);
        mLastAddOrderIdTime = currentTime;
    }

    /**
     * 根据流水号获取订单号
     *
     * @param pTrace 流水号
     * @return 订单号，如果没有找到就返回null
     */
    private synchronized String getOrderId(final String pTrace) {
        if (mOrderMap.containsKey(pTrace)) {
            String value = mOrderMap.get(pTrace);
            //mOrderMap.remove(pTrace);
            return value;
        } else {
            return null;
        }
    }

    //流水号和出货结果的映射
    private HashMap<String, Pair<Integer, Integer>> mResultMap = new HashMap<String, Pair<Integer, Integer>>();
    private long mLastAddResultTime = 0;

    private synchronized void addResult(final String pTrace, final int pResult, final int pErrorCode) {
        long currentTime = System.currentTimeMillis();
        if (currentTime - mLastAddResultTime > 1000 * 60 * 5) //5分钟之后删除掉所有记录
        {
            if (mResultMap.size() > 0) {
                mResultMap.clear();
            }
        }
        Pair<Integer, Integer> pair = new Pair<>(pResult, pErrorCode);
        mResultMap.put(pTrace, pair);
        mLastAddResultTime = currentTime;
    }

    public synchronized Pair<Integer, Integer> getResult(final String pTrace) {
        if (mResultMap.containsKey(pTrace)) {
            Pair<Integer, Integer> value = mResultMap.get(pTrace);
            //mResultMap.remove(pTrace);
            return value;
        } else {
            return null;
        }
    }

    //以下三个方法实现了单例模式，同时保证懒加载和线程安全
    private static class Holder {
        private static DriverThread INSTANCE = new DriverThread();
    }

    private DriverThread() {
//        mContext = Configure.context;
        mlogger = new Log("DriverThread.txt");
        loadSettingFile();
    }

    public static DriverThread getInstance() {
        if (Holder.INSTANCE == null || Holder.INSTANCE.getState() == State.TERMINATED) {
            Holder.INSTANCE = new DriverThread();
        }
        return Holder.INSTANCE;
    }

    public void stopThread() {
        isStopped = true;
    }

    private String setRoadData79(final int box, final byte[] data) {
        ColumnModel[] road = getRoadDataByBox(box);
        String roadStatus = "";// 连着6个byte
        for (int i = 0; i < 6; i++) {
            roadStatus += new StringBuffer(Tools.bytesToBinaryString(data[i + 5])).reverse().toString(); // 48字符
        }
        StringBuilder roadBuffer = new StringBuilder();
        for (int i = 0; i < MAX_ROAD_NUM; i++) {
            ColumnModel model = road[i];
            if (roadStatus.charAt(i) == '0') {
                model.isBreak = false;
            } else {
                model.isBreak = true;
            }
            byte n = data[11 + i];
            int num = 0xFF & n;
            if (num > 99)
                num = 99;
            if (box == BOX_YL) {
                if (model.isBreak) {
                    if (mBox11Stock[i] == 0) {
                        mBox11Stock[i] = 1;
                    }
                } else {
                    if (mBox11Stock[i] != 0) {
                        mBox11Stock[i] = 0;
                    }
                }
                num = mBox11Stock[i];
            }
            model.num = num;
            roadBuffer.append(Tools.addLeftZero(String.valueOf(num), 2)); // 2位一个整型
        }
//        for (int i = 0; i < MAX_ROAD_NUM; i++) {
//            int num = Tools.bytesToInt(data[11 + i]);
//            if (num > 99)
//                roadBuffer.append(String.valueOf(99));
//            else
//                roadBuffer.append(Tools.addLeftZero(String.valueOf(num), 2)); // 2位一个整型
//        }
        return roadStatus + roadBuffer.toString();
    }

    private void setRoadData7E(final int box, final byte[] data) {
        ColumnModel[] road = getRoadDataByBox(box);
        StringBuilder roadBuffer = new StringBuilder();
//        boolean isNullData = true;
        for (int i = 0; i < MAX_ROAD_NUM; i++) {
            ColumnModel model = road[i];
            byte id = data[5 + i];
            model.goodsId = id & 0xFF;
//            if(model.goodsId != 0)
//            {
//                isNullData = false;
//            }
            roadBuffer.append(Tools.bytesToHexString(id));
        }
//        if(!isNullData)
//        {
        mDataMap7E.put(box, roadBuffer.toString());
//        }
    }

    private void setRoadData7F(final int box, final byte[] data) {
        ColumnModel[] road = getRoadDataByBox(box);
        StringBuilder roadBuffer = new StringBuilder();
//        boolean isNullData = true;
        for (int i = 0; i < MAX_ROAD_NUM; i++) {
            ColumnModel model = road[i];
            byte p1 = data[5 + i * 2];
            byte p2 = data[5 + i * 2 + 1];
            int price = 0;
            price = ((0xFF & p1) << 8) + (0xFF & p2);
            model.price = price * 10;
//            if(model.price != 0)
//            {
//                isNullData = false;
//            }
            roadBuffer.append(Tools.addLeftZero(String.valueOf(model.price), 6));
        }
//        if(!isNullData)
//        {
        mDataMap7F.put(box, roadBuffer.toString());
//        }
    }

    private ColumnModel[] getRoadDataByBox(final int box) {
        ColumnModel[] road;
        if (mRoadInfo.containsKey(box)) {
            road = mRoadInfo.get(box);
        } else {
            road = new ColumnModel[MAX_ROAD_NUM];
            for (int i = 0; i < road.length; i++) {
                road[i] = new ColumnModel();
            }
            mRoadInfo.put(box, road);
        }
        return road;
    }

    /**
     * 出货
     *
     * @param pBox        柜号
     * @param pColumn     货到号
     * @param pPrice      价格以分为单位
     * @param pType       出货方式
     * @param pTradeTrace 流水号 6位数字
     * @param pOrderId    订单号
     * @return 指令是否发送成功
     */
    public boolean outGoods(final int pBox, final int pColumn, final int pPrice, final String pType, final String pTradeTrace, final String pOrderId) {
//        if(pBox < 1 || pBox > 11)
//            return false;
        if (pColumn < 1 || pColumn > MAX_ROAD_NUM)
            return false;
        if (pTradeTrace == null || pTradeTrace.length() != 6)
            return false;
        byte[] data = new byte[23];
        data[0] = 0x76;
        data[1] = 0x03;
        data[2] = (byte) pBox;
        data[3] = 0x01;
        data[4] = (byte) pColumn;
        //价格以分为单位
        data[5] = (byte) (pPrice >> 16);
        data[6] = (byte) (pPrice >> 8);
        data[7] = (byte) pPrice;
        //支付方式
        byte[] type = Tools.hexStringToBytes(pType);
        if (type == null || type.length < 1)
            return false;
        data[8] = type[0];
        //流水号
        byte[] bcd = Tools.bcdStringToBytes(pTradeTrace);
        if (bcd == null || bcd.length != 3)
            return false;
        data[9] = bcd[0];
        data[10] = bcd[1];
        data[11] = bcd[2];
        //卡号，10字节，目前填写0x00
        for (int i = 0; i < 10; i++) {
            data[12 + i] = 0x00;
        }
        data[22] = getCheckSum(data, 0, data.length - 1);
        appendQueueData(data);
        addOrderId(pTradeTrace, pOrderId);
        return true;
    }

//    /**
//     * 根据商品ID获取商品信息
//     * @param pId 商品ID 十进制
//     * @return 商品
//     */
//    public GoodsModel getGoodsInfoById(final int pId)
//    {
//        GoodsModel info = getGoodsById(pId);
//        if(info == null)
//            return null;
//        for (GoodsModel model: Configure.goodsList) {
//            if(model.equals(info))
//            {
//                info.setName(model.getName());
//                info.setImage(model.getImage());
//                info.setType(model.getType());
//                info.setBarCode(model.getBarCode());
//                return info;
//            }
//        }
//        return null;
//    }
//
//    /**
//     * 获取全部商品
//     * @return 全部商品的列表
//     */
//    public List<GoodsModel> getGoodsByCategory(final int pCategory)
//    {
//        String category = String.valueOf(pCategory);
//        List<GoodsModel> goodsList = new ArrayList<GoodsModel>();
//
//        //mlogger.i("category:" + category);
//
//        for (GoodsModel model: Configure.goodsList) {
//            //mlogger.i("model:" + model);
//            if(model.getType().equals(category))
//            {
//                int id;
//                try {
//                    id = Integer.parseInt(model.getCode());
//                    GoodsModel info = getGoodsById(id);
//                    if(info != null)
//                    {
//                        info.setName(model.getName());
//                        info.setImage(model.getImage());
//                        info.setType(model.getType());
//                        info.setBarCode(model.getBarCode());
//                        //mlogger.i("model:" + info);
//                        goodsList.add(info);
//                    }
//                }catch (NumberFormatException e)
//                {
//                    //mlogger.e("convert id error:" + e.getMessage());
//                }
//            }
//        }
//        return goodsList;
//    }

    @Override
    public synchronized void start() {
        mlogger.i("current state:" + getState());
        if (isStopped || getState() == State.NEW) {
            super.start();
        }
    }

    @Override
    public void run() {
        super.run();
        if (!isStopped) {
            mlogger.e("driver has aleady running");
            return;
        }
        isStopped = false;
        mlogger.i("driver start");
        try {
            mlogger.i("device:" + Configure.getConfigData(Configure.CONFIG_COMMPORT));
            mlogger.i("baudrate:" + Configure.getConfigData(Configure.CONFIG_BAUDRATE));
            //android 5.1及以上系统
//            mSerialPort = Configure.createSerialPort(Configure.getConfigData(Configure.CONFIG_COMMPORT), Configure.getConfigData(Configure.CONFIG_BAUDRATE));
            //android 4.2系统

            mSerialPort = new SerialPort(new File("/dev/" + Configure.getConfigData(Configure.CONFIG_COMMPORT)), Integer.parseInt(Configure.getConfigData(Configure.CONFIG_BAUDRATE)), 0);
            mOutputStream = mSerialPort.getOutputStream();
            mInputStream = mSerialPort.getInputStream();
            if (mInputStream == null) {
                mlogger.e("can not get the serialport IO stream.");
                isStopped = true;
                return;
            }
            mlogger.i("serial port open");
        }catch(NumberFormatException e){
            e.printStackTrace();
            isStopped=true;
            return;
        } catch (SecurityException e) {
            mlogger.e("You do not have read/write permission to the serialport.");
            isStopped = true;
            return;
        } catch (IOException e) {
            mlogger.e("The serial port can not be opened for an unknownreason.");
            isStopped = true;
            return;
        } catch (InvalidParameterException e) {
            mlogger.e("Please configure your serial port first.");
            isStopped = true;
            return;
        }

        //初始化后台监控服务
        if (Configure.isMonitorOpen) {
            MonitorThread.getInstance().start();
        }

        loadWarrantyData();
        loadBox11Stock();

        //通信开始
        try {
            while (!isStopped) {
                int temp;
                if ((temp = mInputStream.read()) != -1) {
                    int tranLen = getTranTypeLen((byte) temp);
                    if (tranLen > 0) {
                        byte[] readBuffer = new byte[tranLen];
                        int len = 0;
                        readBuffer[len++] = (byte) temp;
                        while (len < tranLen) {
                            int tempLen = mInputStream.read(readBuffer, len, tranLen - len);
                            if (tempLen < 0) {
                                mlogger.e("serial port read data error");
                                //AVM_RUNNING = false;
                                break;
                            }
                            len += tempLen;
                        }
                        if (len < tranLen) {
                            sleep(15);
                            continue;
                        }
                        if (len >= 4) {
                            //检查特征字
                            if (readBuffer[1] != (byte) 0xEF || readBuffer[2] != (byte) 0xEE || readBuffer[3] != (byte) 0xFE) {
                                //非法特征字
                                mlogger.e("Illegal character words:" + Tools.bytesToHexString(readBuffer));
                                //AVM_RUNNING = false;
                                sleep(15);
                                continue;
                            }
                        }
                        doAnalysis(readBuffer);
                    } else {
                        mlogger.e("unknown command:" + Tools.bytesToHexString((byte) temp));
                    }
                } else {
                    mlogger.e("serial port read error");
                }
                sleep(15);
                checkFetchGoodsState();
            }
        } catch (IOException e) {
            mlogger.e("data error !!!!!!!!!!");
        } catch (InterruptedException e) {
            mlogger.e(e.toString());
        }
        if (mSerialPort != null) {
//            Configure.closeSerialPort(mSerialPort);
            mSerialPort.close();
        }
        if (Configure.isMonitorOpen) {
            MonitorThread.getInstance().stopThread();
        }
        mlogger.e("driver stopped");
        isStopped = true;
    }

    private int getTranTypeLen(byte type) {
        int len = 0;

        switch (type) {
            // 定时轮询
            case 0x76:
                len = T76_DATA_LEN;
                break;
            // 签到
            case 0x78:
                len = T78_DATA_LEN;
                break;
            // 机器故障、状态信息，间隔1s
            case 0x79:
                len = T79_DATA_LEN;
                break;
            // 配置信息
            case 0x7A:
                len = T7A_DATA_LEN;
                break;
            // 商品销售汇总信息
            case 0x7B:
                len = T7B_DATA_LEN;
                break;
            // 出货
            case 0x7C:
                len = T7C_DATA_LEN;
                break;
            // 机器运行信息 间隔1s
            case 0x7D:
                len = T7D_DATA_LEN;
                break;
            // 货道商品信息
            case 0x7E:
                len = T7E_DATA_LEN;
                break;
            // 货道价格信息
            case 0x7F:
                len = T7F_DATA_LEN;
                break;
            // 传递料道销售统计
            case 0x75:
                len = T75_DATA_LEN;
                break;
            // 传递售货机标示信息
            case 0x71:
                len = T71_DATA_LEN;
                break;
            // 售货机料道状态信息
            case 0x72:
                len = T72_DATA_LEN;
                break;
            // 售货机外设及系统状态
            case 0x73:
                len = T73_DATA_LEN;
            default:
                break;
        }
        return len;
    }

    private void doAnalysis(byte[] a) {
        //第一个指令不是78就重启主控
        if (isFristCmd) {
            isFristCmd = false;
            if (a[0] != 0x78) {
                noticeAvmRunState(3);
            }
            //resetChargeState(); //初始化充电状态
        }
        switch (a[0]) {
            // 定时轮询
            case 0x76:
                tran76(a);
                break;
            // 签到
            case 0x78:
                tran78(a);
                break;
            // 机器故障、状态信息，间隔1s
            case 0x79:
                tran79(a);
                break;
            // 配置信息
            case 0x7A:
                tran7A(a);
                break;
            // 商品销售汇总信息
            case 0x7B:
                tran7B(a);
                break;
            // 出货
            case 0x7C:
                tran7C(a);
                break;
            // 机器运行信息 间隔1s
            case 0x7D:
                tran7D(a);
                break;
            // 货道商品信息
            case 0x7E:
                tran7E(a);
                break;
            // 货道价格信息
            case 0x7F:
                tran7F(a);
                break;
            // 传递料道销售统计
            case 0x75:
                tran75(a);
                break;
            //传递售货机标示信息
            case 0x71:
                tran71(a);
                break;
            // 售货机料道状态信息
            case 0x72:
                tran72(a);
                break;
            // 售货机外设及系统状态
            case 0x73:
                tran73(a);
                break;
            default:
                mlogger.e("Analysis type error");
                break;
        }
    }

    private void checkFetchGoodsState() {
        if (!Configure.isFetch) {
            return;
        }
        long currentTime = System.currentTimeMillis();
        //取物后3秒钟关闭取物口
        if (mLastFetchGoodsFinishTime > 0 && (currentTime - mLastFetchGoodsFinishTime) > 3000) {
            mlogger.i("fetch goods finish, close door");
            mLastFetchGoodsFinishTime = 0;
            mLastExitDoorOpenTime = 0;
            setDoorLockState(10, 9, 2);
        }
        //一直没取货30秒后关闭取物口
        if (mLastExitDoorOpenTime > 0 && (currentTime - mLastExitDoorOpenTime) > 30000) {
            mlogger.i("fetch goods wait 30 sec, close door");
            mLastFetchGoodsFinishTime = 0;
            mLastExitDoorOpenTime = 0;
            setDoorLockState(10, 9, 2);
        }
    }

    /**
     * 控制取物口或门锁
     *
     * @param pState   10:取物口门控制；11:售货机门锁控制
     * @param pBox     货柜号，只能是9 8 7 6货柜
     * @param pCommand 指令，0表示不动作，1表示打开（或者下降），2表示关闭（或者上升），3表示停止
     * @return 指令是否成功
     */
    public boolean setDoorLockState(final int pState, final int pBox, final int pCommand) {
        if (pState != 10 && pState != 11) {
            return false;
        }
        if (pBox < 6 || pBox > 9) {
            return false;
        }
        if (pCommand < 0 || pCommand > 3) {
            return false;
        }
        int cmd = pCommand;
        cmd = cmd << ((pBox - 6) * 2);
        byte[] dataBytes = new byte[5];
        dataBytes[0] = 0x76;
        dataBytes[1] = 0x07;
        dataBytes[2] = (byte) pState; //设置取物口或门锁
        dataBytes[3] = (byte) cmd;
        dataBytes[4] = 0x00; //CRC
        dataBytes[4] = getCheckSum(dataBytes);

        appendQueueData(dataBytes); //采用队列的方式
        return true;
    }

    //轮询 76
    private void tran76(byte[] data) {
        if (!checkSum(T76_DATA_LEN, data)) {
            return;
        }
        mlogger.i("76: " + Tools.bytesToHexString(data));
        //AVM_RUNNING = true;
        // 用队列的方式来获取主动指令
        byte[] queueData = getQueueData();
        if (queueData != null) {
            int ret = doSendData(queueData);
            if (ret == 0) {
                cmdResult = CMD_ACK;
            } else {
                if (ret == -3 || ret == -2) {
                    if (outGoodsCount > 0) {
                        //-3或-2表示当前主动指令没有收到ACK或NAK，因此重新发送
                        appendQueueData(queueData);
                        outGoodsCount--;
                    } else {
                        mlogger.e("主动指令重试次数用完");
                        outGoodsCount = OUT_GOODS_COUNT;
                        cmdResult = CMD_NAK;
                    }
                    return;
                } else {
                    cmdResult = CMD_NAK;
                }
            }
            outGoodsCount = OUT_GOODS_COUNT;
            return;
        }
        defaultAck(data);
        //76EFEEFE000000000000000000000253
        //接收到的金额
        int totalMoney = Tools.bytesToInt(Tools.subByte(data, 4, 6));
        int paperMoney = Tools.bytesToInt(Tools.subByte(data, 6, 8));
        int coinMoney = Tools.bytesToInt(Tools.subByte(data, 8, 10));
        int box = data[10]; //货柜
        int column = data[11]; //货道
        int goods = data[12]; //商品编号
        int price = Tools.bytesToInt(Tools.subByte(data, 13, 15)) * 10; //价格，转换为分为单位
        //判断是否接收到的金额改变了
        boolean isReceiveMoneyChanged = false;
        if (this.totalMoney != totalMoney) {
            isReceiveMoneyChanged = true;
            this.totalMoney = totalMoney;
            this.paperMoney = paperMoney;
            this.coinMoney = coinMoney;
        }
        if (isReceiveMoneyChanged) {
            //发送接收到金额广播
            sendReceiveMoneyToListener(totalMoney, paperMoney, coinMoney);
//            if(mContext != null) {
//                Intent intent = new Intent(INTENT_RECEIVE_MONEY);
//                mContext.sendBroadcast(intent);
//            }
        }
        //判断是否是按键选货
        if (box != 0 && column != 0 && goods != 0) {
            long current = System.currentTimeMillis();
            int protectTime;
            try {
                protectTime = Integer.parseInt(Configure.getConfigData(Configure.CONFIG_PROTECT_TIME));
            } catch (NumberFormatException e) {
                protectTime = 5000;
            }
            if ((current - mSelectTime) < protectTime && box == mBox && column == mColumn && goods == mGoods) //在保护时间内如果按键选货是同一个那么就忽略
            {
                return;
            }
            mlogger.i("select goods:" + String.valueOf(box) + " " + String.valueOf(column));
            sendGoodsSelectedToListener(box, column, goods, price);
//            if(mContext != null) {
//                //发送按键选货广播
//                Intent intent = new Intent(INTENT_SELECT_GOODS);
//                intent.putExtra(EXTRA_SELECT_GOODS_BOX, box);
//                intent.putExtra(EXTRA_SELECT_GOODS_COLUMN, column);
//                intent.putExtra(EXTRA_SELECT_GOODS_ID, goods);
//                intent.putExtra(EXTRA_SELECT_GOODS_PRICE, price);
//                mContext.sendBroadcast(intent);
//            }
        } else if (box == 0 && column == 0 && goods == 0 && price == 10) //有人接近(人体感应)
        {
            //未实现
        } else if (box == 0 && column == 0 && goods == 0 && price == 20) //游戏键被按下
        {
            sendOnGameButtonPressedToListener();
        } else if (box == 0 && column == 0 && goods == 0 && price == 30) //按了退币杆
        {
            sendOnCoinReleaseLeverToListener();
        }
        if (Configure.isMonitorOpen) {
            MonitorThread.getInstance().addMessage52();
        }
    }

    //签到 78
    private void tran78(byte[] data) {
        mCmd78Count++;
        if (!checkSum(T78_DATA_LEN, data)) {
            return;
        }
        mlogger.i("78: " + Tools.bytesToHexString(data));
        //AVM_RUNNING = true;
//        defaultAck(data);

        // 主控板版本号
        String avmVersion = Tools.bytesToHexString(Tools.subByte(
                data, 4, 6));
        Configure.setAvmVersion(avmVersion);
        mlogger.i("avm version is : " + avmVersion);
        if (avmVersion.charAt(0) != '0') {
            //发送加密后的ack
            encryptAck(data);
        } else {
            defaultAck(data);
        }

        // 售货机编号
        String machNo = Tools.bytesToBcdString(Tools.subByte(data, 6, 10));
        if (machNo != null && !machNo.equals("00000000") && machNo.length() == 8) {
            FileTools.setMac2File(machNo);
            Configure.setMacNo(machNo);
        } else {
            mlogger.e("get mac number from file");
            String mac = FileTools.getMacFromFile();
            if (mac == null) {
                mlogger.e("can't get mac number");
                Configure.setMacNo("00000000");
            } else {
                Configure.setMacNo(mac);
            }
        }
        // 机器类型
        String machType = Tools.bytesToHexString(data[17]);
        Configure.setMacType(machType);
        // 饮料机数量
        String drinkNum = String.valueOf(data[18]);
        Configure.setDrinkNum(drinkNum);
        // 食品机数量
        String foodNum = String.valueOf(data[19]);
        Configure.setFoodsNum(foodNum);
        // 泡面柜数量
        String noodleNum = String.valueOf(data[20]);
        Configure.setNoodleNum(noodleNum);
        // 饮料机货道数设置
        String drinkRoadNum = String.valueOf(data[21]);
        Configure.setDrinkColumnNum(drinkRoadNum);
        // 食品机货道数设置
        String foodRoadNum = String.valueOf(data[22]);
        Configure.setFoodsColumnNum(foodRoadNum);
        // 泡面柜货道数设置
        String noodleRoadNum = String.valueOf(data[23]);
        Configure.setNoodleColumnNum(noodleRoadNum);
        // 第一个饮料机货道数
        Configure.setColumnNum(11, data[21] & 0xFF);
        // 第一个食品机货道数
        Configure.setColumnNum(9, data[22] & 0xFF);
        // 第一个商品柜货道数
        Configure.setColumnNum(1, data[23] & 0xFF);
        if (Configure.isMonitorOpen) {
            MonitorThread.getInstance().addMessage51(machType, drinkNum, foodNum, noodleNum, drinkRoadNum, foodRoadNum, noodleRoadNum);
        }
//        if(mCmd78Count > 1) //如果又收到了78指令，那么发送当前充电状态给主控
//        {
//            byte[] dataBytes = new byte[5];
//            dataBytes[0] = 0x76;
//            dataBytes[1] = 0x07;
//            dataBytes[2] = 0x09; //设置充电状态
//            dataBytes[3] = mChargeState;
//            dataBytes[4] = 0x00; //CRC
//            dataBytes[4] = getCheckSum(dataBytes);
//            appendQueueData(dataBytes); //采用队列的方式
//        }
    }

    private void encryptAck(byte[] data) {
        byte sumByte = 0x00;
        for (int i = 0; i < 13; i++) {
            sumByte += data[4 + i];
        }
//        mlogger.i("sum byte:" + Tools.bytesToHexString(sumByte));
        byte key[] = {(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
                (byte) 0x3C, (byte) 0x7B, (byte) 0x4D, (byte) 0xF9, (byte) 0xCF,
                (byte) 0x09, (byte) 0x13, (byte) 0xA5, (byte) 0xEE, (byte) 0xFF, (byte) 0x45,
                (byte) 0x7A, (byte) 0x96, (byte) 0xE4, (byte) 0xE6, (byte) 0xB3};
//        key[key.length - 1] = sumByte;
        key[0] = sumByte;
        String md5 = MD5.getBytesMD5(key);
        if (md5 == null || "".equals(md5)) {
            mlogger.e("MD5计算失败");
            defaultAck(data);
            return;
        }
//        mlogger.i("md5:" + md5 );
        byte hexArray[] = Tools.hexStringToBytes(md5);
        sumByte = 0x00;
        for (int i = 0; i < hexArray.length; i++) {
            sumByte += hexArray[i];
        }
        //发送ack
        byte[] afkBytes = new byte[3];
        afkBytes[0] = data[0];
        afkBytes[1] = sumByte;
        afkBytes[2] = (byte) (afkBytes[0] + afkBytes[1]);
        mlogger.d("Ack :" + Tools.bytesToHexString(afkBytes));
        try {
            mOutputStream.write(afkBytes);
            mOutputStream.flush();
        } catch (IOException e) {
            mlogger.e("send ack data failed!");
        }
    }

    //料道运行信息 79
    private void tran79(byte[] data) {
        try {
            if (!checkSum(T79_DATA_LEN, data)) {
                return;
            }
            mlogger.i("79: " + Tools.bytesToHexString(data));
            defaultAck(data);
            int box = 0xFF & data[4];
            String roadString = setRoadData79(box, data);
            sendOnFreshColumnToListener();
//        if(mContext != null) {
//            Intent intent = new Intent(INTENT_REFRESH_ROAD);
//            mContext.sendBroadcast(intent);
//        }
            if (Configure.isMonitorOpen) {
                MonitorThread.getInstance().addMessage62(box, roadString);
            }
            if (box == BOX_YL) {
                saveBox11Stock();
            }
        } catch (OutOfMemoryError e) {
            e.printStackTrace();
        }
    }

    //系统配置信息 7A
    private void tran7A(byte[] data) {
        if (!checkSum(T7A_DATA_LEN, data)) {
            return;
        }
        mlogger.i("7A: " + Tools.bytesToHexString(data));
        defaultAck(data);

        int boxNo = data[4];
        String config01 = Tools.bytesToBinaryString(data[5]); // 售卖配置
        String config02 = Tools.bytesToHexString(data[6]); // 照明状态
        String config03 = Tools.bytesToBcdString(Tools.subByte(data,
                7, 11)); // 灯时间
        String config04 = Tools.bytesToHexString(data[11]); // 左室
        String config05 = Tools.bytesToHexString(data[12]); // 右室
        String config06 = Tools.bytesToBcdString(Tools.subByte(data,
                13, 17)); // 节能时间
        String config07 = Tools.bytesToBinaryString(data[17]);
        config07 = ("0".equals(config07.substring(0, 1)) ? "+" : "-")
                + Tools.addLeftZero(
                String.valueOf(Integer.parseInt(
                        config07.substring(1), 2)), 2);
        String config08 = Tools.bytesToBinaryString(data[18]);
        config08 = ("0".equals(config08.substring(0, 1)) ? "+" : "-")
                + Tools.addLeftZero(
                String.valueOf(Integer.parseInt(
                        config08.substring(1), 2)), 2);
        String config09 = Tools.bytesToBinaryString(data[19]);
        config09 = ("0".equals(config09.substring(0, 1)) ? "+" : "-")
                + Tools.addLeftZero(
                String.valueOf(Integer.parseInt(
                        config09.substring(1), 2)), 2);
        String config10 = Tools.bytesToBinaryString(data[20]);
        config10 = ("0".equals(config10.substring(0, 1)) ? "+" : "-")
                + Tools.addLeftZero(
                String.valueOf(Integer.parseInt(
                        config10.substring(1), 2)), 2);

        StringBuilder configMessage = new StringBuilder();
        configMessage.append(config01).append(config02).append(config03)
                .append(config04).append(config05).append(config06)
                .append(config07).append(config08).append(config09)
                .append(config10);
        String monitorStatus = configMessage.toString();
        String data7A = mDataMap7A.get(boxNo);
        if (data7A == null || !data7A.equals(monitorStatus)) {
            mDataMap7A.put(boxNo, monitorStatus);
        }
        if (Configure.isMonitorOpen) {
            MonitorThread.getInstance().addMessage63(boxNo, monitorStatus);
        }
    }

    //商品销售汇总信息 7B
    private void tran7B(byte[] data) {
        if (!checkSum(T7B_DATA_LEN, data)) {
            return;
        }
        mlogger.i("7B: " + Tools.bytesToHexString(data));
        defaultAck(data);
        if (Configure.isMonitorOpen) {
            String sale01 = Tools.addLeftZero(String.valueOf(Tools
                    .bytesToInt(Tools.subByte(data, 4, 7))), 8);
            String sale02 = Tools.addLeftZero(String.valueOf(Tools
                    .bytesToLong(Tools.subByte(data, 7, 11))), 10);
            String sale03 = Tools.addLeftZero(String.valueOf(Tools
                    .bytesToInt(Tools.subByte(data, 11, 14))), 8);
            String sale04 = Tools.addLeftZero(String.valueOf(Tools
                    .bytesToLong(Tools.subByte(data, 14, 18))), 10);
            String sale05 = Tools.addLeftZero(String.valueOf(Tools
                    .bytesToInt(Tools.subByte(data, 18, 21))), 8);
            String sale06 = Tools.addLeftZero(String.valueOf(Tools
                    .bytesToLong(Tools.subByte(data, 21, 25))), 10);
            String sale07 = Tools.addLeftZero(String.valueOf(Tools
                    .bytesToInt(Tools.subByte(data, 25, 28))), 8);
            String sale08 = Tools.addLeftZero(String.valueOf(Tools
                    .bytesToLong(Tools.subByte(data, 28, 32))), 10);
            String sale09 = Tools.addLeftZero(String.valueOf(Tools
                    .bytesToInt(Tools.subByte(data, 32, 35))), 8);
            String sale10 = Tools.addLeftZero(String.valueOf(Tools
                    .bytesToLong(Tools.subByte(data, 35, 39))), 10);
            String sale11 = Tools.addLeftZero(String.valueOf(Tools
                    .bytesToInt(Tools.subByte(data, 39, 42))), 8);
            String sale12 = Tools.addLeftZero(String.valueOf(Tools
                    .bytesToLong(Tools.subByte(data, 42, 46))), 10);
            String sale13 = Tools.addLeftZero(String.valueOf(Tools
                    .bytesToInt(Tools.subByte(data, 46, 49))), 8);
            String sale14 = Tools.addLeftZero(String.valueOf(Tools
                    .bytesToLong(Tools.subByte(data, 49, 53))), 10);
            String sale15 = Tools.addLeftZero(String.valueOf(Tools
                    .bytesToInt(Tools.subByte(data, 53, 55))), 4);
            String sale16 = Tools.addLeftZero(String.valueOf(Tools
                    .bytesToInt(Tools.subByte(data, 55, 58))), 8);
            String sale17 = Tools.addLeftZero(String.valueOf(Tools
                    .bytesToInt(Tools.subByte(data, 58, 60))), 4);
            String sale18 = Tools.addLeftZero(String.valueOf(Tools
                    .bytesToInt(Tools.subByte(data, 60, 63))), 8);
            String sale19 = Tools.addLeftZero(String.valueOf(Tools
                    .bytesToInt(Tools.subByte(data, 63, 65))), 4);
            String sale20 = Tools.addLeftZero(String.valueOf(Tools
                    .bytesToInt(Tools.subByte(data, 65, 68))), 8);
            String sale21 = Tools.addLeftZero(String.valueOf(Tools
                    .bytesToInt(Tools.subByte(data, 68, 70))), 4);
            String sale22 = Tools.addLeftZero(String.valueOf(Tools
                    .bytesToInt(Tools.subByte(data, 70, 73))), 8);
            String clearTime = Tools.bytesToBcdString(Tools.subByte(data,
                    73, 80));

            StringBuilder saleMessage = new StringBuilder();
            saleMessage.append(sale01).append(sale02).append(sale03)
                    .append(sale04).append(sale05).append(sale06)
                    .append(sale07).append(sale08).append(sale09)
                    .append(sale10).append(sale11).append(sale12)
                    .append(sale13).append(sale14).append(sale15)
                    .append(sale16).append(sale17).append(sale18)
                    .append(sale19).append(sale20).append(sale21)
                    .append(sale22).append(clearTime);
            String monitorStatus = saleMessage.toString();
            MonitorThread.getInstance().addMessage72(monitorStatus);
        }
    }

    //出货 7C
    private void tran7C(byte[] data) {
        if (!checkSum(T7C_DATA_LEN, data)) {
            return;
        }
        mlogger.i("7C: " + Tools.bytesToHexString(data));
        defaultAck(data);

        String boxNo = Tools.addLeftZero(String.valueOf(data[4]), 2);
        String roadNo = Tools.addLeftZero(String.valueOf(data[5]), 2);
        int box = data[4];
        int column = data[5];
        final String goodsCode = Tools.bytesToHexString(data[6]);
        int price = Tools.bytesToInt(Tools.subByte(data, 7, 10));
        String tranMoney = Tools.addLeftZero(String.valueOf(price), 6); // 售卖的金额6位
        // 出货结果
        final int tranResult = data[12];
        int errorCode = data[13];
        String tranCard = Tools.bytesToHexString(Tools.subByte(data, 14, 23)); // 除了一卡通外的卡号信息
        tranCard = tranCard.substring(0, 18);
        // 流水号
        String tradeTrace = Tools.bytesToBcdString(Tools.subByte(data, 23, 26));
        if (tradeTrace.length() > 6)
            tradeTrace = tradeTrace.substring(0, 6);
        //序列号
        String machTrace = Tools.addLeftZero(String.valueOf(Tools.bytesToInt(data[10])), 3);
        //这里判断下序列号，如果和上次的一样就不往后处理
        if (machTrace.equals(mTraceNo)) {
            return;
        }
        mTraceNo = machTrace;
        //出货方式
        String payType = Tools.addLeftZero(Tools.bytesToHexString(data[11]), 2);
        int t = Integer.parseInt(payType, 16);
        String totalNum = Tools.addLeftZero(String.valueOf(Tools.bytesToLong(Tools.subByte(data, 26, 29))), 8); // 销售的总数量
        String totalAmt = Tools.addLeftZero(String.valueOf(Tools.bytesToLong(Tools.subByte(data, 29, 33))), 10); // 销售的总金额

        //加入我们内部的商户订单号
        String orderId = getOrderId(tradeTrace);
        if (Configure.isMonitorOpen) {
            //新加入的商户订单号
            if (orderId != null && !"".equals(orderId)) {
                int len = orderId.length();
                if (len > 64) {
                    orderId = orderId.substring(0, 64);
                } else if (len < 64) {
                    for (int i = 64 - len; i > 0; i--) {
                        orderId += " ";
                    }
                }
            } else {
                // 非支付宝微信支付上传交易流水号
                orderId = Configure.getMacNo() + Tools.getCurrentTime();
                if (orderId != null && !"".equals(orderId)) {
                    int len = orderId.length();
                    if (len > 64) {
                        orderId.substring(0, 64);
                    } else if (len < 64) {
                        for (int i = 64 - len; i > 0; i--) {
                            orderId += " ";
                        }
                    }
                }
            }
        }

        //饮料机和食品机光感出货失败
        if (((box == BOX_YL && tranResult == 1) || (tranResult == 3 && (box <= 9 && box >= 6))) && mBox11MessageMap == null && mBox11Retry) {
            mlogger.i("//饮料机和食品机光感出货失败");
            if (t > 21) {
                mlogger.i("//饮料机和食品机光感出货失败>21");
                mBox11MessageMap = new HashMap<>();
                mBox11MessageMap.put("goodsCode", goodsCode);
                mBox11MessageMap.put("tranMoney", tranMoney);
                mBox11MessageMap.put("machTrace", machTrace);
                mBox11MessageMap.put("payType", payType);
                mBox11MessageMap.put("tranCard", tranCard);
                mBox11MessageMap.put("tradeTrace", tradeTrace);
                mBox11MessageMap.put("totalNum", totalNum);
                mBox11MessageMap.put("totalAmt", totalAmt);
                mBox11MessageMap.put("orderId", orderId);
                //再出一次货
                SimpleDateFormat sd = new SimpleDateFormat("HHmmss", Locale.US);
                String trace = sd.format(new Date());
                String o = String.valueOf(System.currentTimeMillis());
                outGoods(box, column, price, OUT_GOODS_TIHUO, trace, o);
                mlogger.i("outgoods again box:" + boxNo + " column:" + roadNo + " trace:" + trace);
            } else if (t < 21) {
                mlogger.i("//饮料机和食品机光感出货失败<21");
                mBox11MessageMap = new HashMap<>();
                mBox11MessageMap.put("goodsCode", goodsCode);
                mBox11MessageMap.put("tranMoney", tranMoney);
                mBox11MessageMap.put("machTrace", machTrace);
                mBox11MessageMap.put("payType", payType);
                mBox11MessageMap.put("tranCard", tranCard);
                mBox11MessageMap.put("tradeTrace", tradeTrace);
                mBox11MessageMap.put("totalNum", totalNum);
                mBox11MessageMap.put("totalAmt", totalAmt);
                mBox11MessageMap.put("orderId", orderId);
                //再出一次货
                SimpleDateFormat sd = new SimpleDateFormat("HHmmss", Locale.US);
                String trace = sd.format(new Date());
                String o = String.valueOf(System.currentTimeMillis());
                outGoods(box, column, price, OUT_GOODS_CASH, trace, o);
                mlogger.i("outgoods again box:" + boxNo + " column:" + roadNo + " trace:" + trace);
            }
        } else {
            if ((payType.equals(OUT_GOODS_TIHUO) || payType.equals(OUT_GOODS_CASH)) && mBox11MessageMap != null) {
                mlogger.i("send new 71");
                if (Configure.isMonitorOpen)
                    MonitorThread.getInstance().addMessage12(boxNo, roadNo, mBox11MessageMap.get("goodsCode"), tranMoney, mBox11MessageMap.get("machTrace"), mBox11MessageMap.get("payType"), tranResult, errorCode, mBox11MessageMap.get("tranCard"), mBox11MessageMap.get("tradeTrace"), mBox11MessageMap.get("totalNum"), mBox11MessageMap.get("totalAmt"), mBox11MessageMap.get("orderId"));
                if (tranResult <= 4) { //出货结果只有5以下的结果才发送
                    addResult(mBox11MessageMap.get("tradeTrace"), tranResult, errorCode);
                }
                sendOnOutGoodsListenerToListener(tranResult, data[4], data[5], data[6], Tools.bytesToInt(Tools.subByte(data, 7, 10)), mBox11MessageMap.get("tradeTrace"), mBox11MessageMap.get("machTrace"), mBox11MessageMap.get("payType"), errorCode);
                mBox11MessageMap.clear();
                mBox11MessageMap = null;
            } else {
                mlogger.i("出货结果==3");
                if (Configure.isMonitorOpen)
                    MonitorThread.getInstance().addMessage12(boxNo, roadNo, goodsCode, tranMoney, machTrace, payType, tranResult, errorCode, tranCard, tradeTrace, totalNum, totalAmt, orderId);
                if (tranResult <= 4) { //出货结果只有5以下的结果才发送
                    addResult(tradeTrace, tranResult, errorCode);
                }
                mlogger.i("出货结果==4: " + tradeTrace);
                sendOnOutGoodsListenerToListener(tranResult, data[4], data[5], data[6], Tools.bytesToInt(Tools.subByte(data, 7, 10)), tradeTrace, machTrace, payType, errorCode);
            }
            if (mBoxLock && ((box == BOX_YL && tranResult == 1) || (tranResult == 3 && (box <= 9 && box >= 6)))) {
                mlogger.i("货道锁定");
                //饮料机出货结果为1，食品机出货结果为3时，将货道锁定
                //先找到这个货道的价格
                if (mRoadInfo.containsKey(box)) {
                    ColumnModel[] road;
                    road = mRoadInfo.get(box);
                    if (column - 1 < road.length) {
                        price = road[column - 1].price;
                    }
                }
                price = price / 10;
                byte[] byteArray = makePriceAndCodeByteArray(box, column, price, DriverThread.COLUMN_DISABLED, false);
                mlogger.i("lock road");
                appendQueueData(byteArray);
                String json = getGoodsInfo(box, column);
                if (null != json && !"".equals(json)) {
                    GoodsModel model = GoodsModel.fromJson(json);
                    model.setCode(String.valueOf(COLUMN_DISABLED));
                    addDisabledGoods(model);
                }
            }
        }

        decrementWarranty(box, column);
        saveWarrantyData();
        if (box == BOX_YL) {
            mBox11Stock[column - 1]--;
            if (mBox11Stock[column - 1] < 0) {
                mBox11Stock[column - 1] = 0;
            }
        }
        if (Configure.isMonitorOpen) {
            MonitorThread.getInstance().addMessage84(box, getWarrantyFromBox(box));
        }
        if (Configure.isFetch && tranResult == 0) //出货成功后打开取物口
        {
            setDoorLockState(10, 9, 1);
            mLastExitDoorOpenTime = System.currentTimeMillis();
            mlogger.i("fetch goods door open");
        }

        /*拿到出货商品的国标码信息barcode
        * int box = data[4];
             int column = data[5];
        * */
        mlogger.i("tran7c box:" + box + ",column:" + column);
    }

    //机器运行信息 7D
    private void tran7D(byte[] data) {
        if (!checkSum(T7D_DATA_LEN, data)) {
            return;
        }
        mlogger.i("7D: " + Tools.bytesToHexString(data));
        defaultAck(data);

        String runStatus = Tools.bytesToBinaryString(data[4]); // 第8位营业状态，第7位门状态
        String avmStatus = Tools.bytesToBinaryString(data[5]); // 售货机状态
        String billStatus = Tools.bytesToBinaryString(data[6]); // 纸币器状态
        String coinStatus = Tools.bytesToBinaryString(data[7]); // 硬币器状态
        String coin5Num = Tools.addLeftZero(String.valueOf(Tools.bytesToInt(Tools.subByte(data, 8, 10))), 4); // 4位5分硬币数量
        String coin10Num = Tools.addLeftZero(String.valueOf(Tools.bytesToInt(Tools.subByte(data, 10, 12))), 4); // 4位一元硬币数量
        String billNum = Tools.addLeftZero(String.valueOf(Tools.bytesToInt(Tools.subByte(data, 12, 14))), 4); // 纸币数量

        mData7D = runStatus + avmStatus + billStatus + coinStatus + coin5Num + coin10Num + billNum;

        char door = runStatus.charAt(6);
        if (door == '1') //门开
        {
            if (doorState != DOOR_OPEN) {
                doorState = DOOR_OPEN;
                mlogger.i("door open");
                sendOnDoorStateChangedToListener(true);
//                if(mContext != null) {
//                    //发送门状态广播
//                    Intent intent = new Intent(INTENT_DOOR_STATE);
//                    mContext.sendBroadcast(intent);
//                }
            }
        } else //门关
        {
            if (doorState != DOOR_CLOSE) {
                doorState = DOOR_CLOSE;
                mlogger.i("door close");
                sendOnDoorStateChangedToListener(false);
//                if(mContext != null) {
//                    //发送门状态广播
//                    Intent intent = new Intent(INTENT_DOOR_STATE);
//                    mContext.sendBroadcast(intent);
//                }
            }
        }

        char fetchGoods = avmStatus.charAt(3);
        if (fetchGoods == '1') //待取货状态
        {
            if (fetchGoodsState != FETCH_GOODS_WAIT) {
                fetchGoodsState = FETCH_GOODS_WAIT;
                mlogger.i("fetch goods wait");
            }
        } else //取完货状态
        {
            if (fetchGoodsState != FETCH_GOODS_FINISH) {
                fetchGoodsState = FETCH_GOODS_FINISH;
                mlogger.i("fetch goods finish");
                mLastFetchGoodsFinishTime = System.currentTimeMillis();
            }
        }

        if (Configure.isMonitorOpen) {
            StringBuilder runMessage = new StringBuilder();
            runMessage.append("0").append(runStatus).append(avmStatus).append(billStatus).append(coinStatus).append(coin5Num).append(coin10Num).append(billNum);
            MonitorThread.getInstance().addMessage61(runMessage.toString());
        }
    }

    //料道商品信息 7E
    private void tran7E(byte[] data) {
        if (!checkSum(T7E_DATA_LEN, data)) {
            return;
        }
        mlogger.i("7E: " + Tools.bytesToHexString(data));
        defaultAck(data);
        int box = 0xFF & data[4];
        setRoadData7E(box, data);
        if (Configure.isMonitorOpen) {
            String data7e;
            String data7f;
            if (mDataMap7E.containsKey(box)) {
                data7e = mDataMap7E.get(box);
            } else {
                return;
            }
            if (mDataMap7F.containsKey(box)) {
                data7f = mDataMap7F.get(box);
            } else {
                return;
            }
            MonitorThread.getInstance().addMessage64(box, data7e, data7f);
            MonitorThread.getInstance().addMessage11(box, data7e, data7f);
        }
    }

    //料道价格信息 7F
    private void tran7F(byte[] data) {
        if (!checkSum(T7F_DATA_LEN, data)) {
            return;
        }
        mlogger.i("7F: " + Tools.bytesToHexString(data));
        defaultAck(data);
        int box = 0xFF & data[4];
        setRoadData7F(box, data);
        if (Configure.isMonitorOpen) {
            String data7e;
            String data7f;
            if (mDataMap7E.containsKey(box)) {
                data7e = mDataMap7E.get(box);
            } else {
                return;
            }
            if (mDataMap7F.containsKey(box)) {
                data7f = mDataMap7F.get(box);
            } else {
                return;
            }
            MonitorThread.getInstance().addMessage64(box, data7e, data7f);
            MonitorThread.getInstance().addMessage11(box, data7e, data7f);
        }
    }

    //传递料道销售统计 75
    private void tran75(byte[] data) {
        if (!checkSum(T75_DATA_LEN, data)) {
            return;
        }
        mlogger.i("75: " + Tools.bytesToHexString(data));
        defaultAck(data);
    }

    private void tran71(byte[] data) // 传递售货机标示信息
    {
        mlogger.i("71: " + Tools.bytesToHexString(data));
        if (!checkSum(T71_DATA_LEN, data)) {
            return;
        }

        defaultAck(data);

        byte[] b = new byte[12];
        System.arraycopy(data, 4, b, 0, b.length);
        String uuid = Tools.bytesToHexString(b);
        Configure.setUUID(uuid);

        String drink2Num = Tools.addLeftZero(String.valueOf(Tools.bytesToInt(data[16])), 2); //饮料机2实际料道数量
        String food2Num = Tools.addLeftZero(String.valueOf(Tools.bytesToInt(data[17])), 2); //食品机2实际料道数量
        String food3Num = Tools.addLeftZero(String.valueOf(Tools.bytesToInt(data[18])), 2); //食品机3实际料道数量
        String food4Num = Tools.addLeftZero(String.valueOf(Tools.bytesToInt(data[19])), 2); //食品机4实际料道数量
        // 第2个饮料机货道数
        Configure.setColumnNum(10, data[16] & 0xFF);
        // 第2个食品机货道数
        Configure.setColumnNum(8, data[17] & 0xFF);
        // 第3个食品机货道数
        Configure.setColumnNum(7, data[18] & 0xFF);
        // 第4个食品机货道数
        Configure.setColumnNum(6, data[19] & 0xFF);
        //预留10位
        String reserved = "";
//        for(int i = 0;i < 5;i++)
//        {
//            reserved += Tools.addLeftZero(String.valueOf(Tools.bytesToInt(data[20 + i])), 2);
//        }
        b = new byte[5];
        System.arraycopy(data, 20, b, 0, b.length);
        reserved = Tools.bytesToHexString(b);
        String cashMinValue = Tools.addLeftZero(String.valueOf(Tools.bytesToInt(data[25])), 3); //现金最小面额
        String cashDigital = Tools.bytesToHexString(data[26]); //Tools.addLeftZero(String.valueOf(Tools.bytesToInt(data[26])), 2); //现金小数点位数
        //硬币器6个识别面额
        String coinValue = "";
//        for(int i = 0;i < 6;i++)
//        {
//            coinValue += Tools.addLeftZero(String.valueOf(Tools.bytesToInt(data[27 + i])), 2);
//        }
        b = new byte[6];
        System.arraycopy(data, 27, b, 0, b.length);
        coinValue = Tools.bytesToHexString(b);
        //纸币器6个识别面额
        String paperValue = "";
//        for(int i = 0;i < 6;i++)
//        {
//            paperValue += Tools.addLeftZero(String.valueOf(Tools.bytesToInt(data[33 + i])), 2);
//        }
        b = new byte[6];
        System.arraycopy(data, 33, b, 0, b.length);
        paperValue = Tools.bytesToHexString(b);
        //预留6位
        String reserved2 = "";
//        for(int i = 0;i < 3;i++)
//        {
//            reserved2 += Tools.addLeftZero(String.valueOf(Tools.bytesToInt(data[39 + i])), 2);
//        }
        b = new byte[3];
        System.arraycopy(data, 39, b, 0, b.length);
        reserved2 = Tools.bytesToHexString(b);

        if (Configure.isMonitorOpen) {
            StringBuilder runMessage = new StringBuilder();
            runMessage.append(uuid).append(drink2Num).append(food2Num)
                    .append(food3Num).append(food4Num).append(reserved)
                    .append(cashMinValue).append(cashDigital).append(coinValue)
                    .append(paperValue).append(reserved2);
            String runInfo = runMessage.toString();
            MonitorThread.getInstance().addMessage81(runInfo);
        }
    }

    // 售货机机料道状态信息
    private void tran72(byte[] data) {
        mlogger.i("72: " + Tools.bytesToHexString(data));
        if (!checkSum(T72_DATA_LEN, data)) {
            return;
        }

        defaultAck(data);

        int boxNo = data[4];
        ColumnModel[] roadArray = getRoadDataByBox(boxNo);
        //食品机弹簧、履带料道信息；饮料机电磁铁、电机料道信息
        String roadInfo = "";// 连着6个byte
        for (int i = 0; i < 6; i++) {
            roadInfo += new StringBuffer(Tools.bytesToBinaryString(data[i + 5])).reverse().toString(); // 48字符
        }
        //单双电机（电磁铁）料道
        String roadMotor = "";// 连着6个byte
        for (int i = 0; i < 6; i++) {
            roadMotor += new StringBuffer(Tools.bytesToBinaryString(data[i + 11])).reverse().toString(); // 48字符
        }
        //料道是否故障
        String roadBreak = "";// 连着6个byte
        for (int i = 0; i < 6; i++) {
            roadBreak += new StringBuffer(Tools.bytesToBinaryString(data[i + 17])).reverse().toString(); // 48字符
        }
        //料道是否设定
        String roadSet = "";// 连着6个byte
        for (int i = 0; i < 6; i++) {
            roadSet += new StringBuffer(Tools.bytesToBinaryString(data[i + 23])).reverse().toString(); // 48字符
        }
        //有货无货状态 //TODO
        String roadState = "";// 连着6个byte
        for (int i = 0; i < 6; i++) {
            roadState += new StringBuffer(Tools.bytesToBinaryString(data[i + 29])).reverse().toString(); // 48字符
        }
        //料道是否禁用
        String roadEnable = "";// 连着6个byte
        for (int i = 0; i < 6; i++) {
            roadEnable += new StringBuffer(Tools.bytesToBinaryString(data[i + 35])).reverse().toString(); // 48字符
        }
        //料道制冷加热状态
        String roadTemp = "";// 连着12个byte
        //mlogger.i("temperature state:" + String.valueOf(boxNo));
        for (int i = 0; i < 12; i++) {
            String road = new StringBuffer(Tools.bytesToBinaryString(data[i + 41])).toString();
            //mlogger.i("index:" + String.valueOf(i) + " road:" + road);
            for (int j = 3; j >= 0; j--) {
                int temp = Integer.parseInt(road.substring(j * 2, j * 2 + 2), 2);
                ColumnModel column = roadArray[i * 4 + (3 - j)];
                column.temperatureState = temp;
                //mlogger.i("column:" + String.valueOf(i * 4 + (3 - j)) + " t:" + String.valueOf(temp));
                roadTemp += String.valueOf(temp); // 48字符
            }
        }
        //食品机设备状态信息
        String foodState = new StringBuffer(Tools.bytesToBinaryString(data[53])).reverse().toString();
        foodState += new StringBuffer(Tools.bytesToBinaryString(data[54])).reverse().toString();
        foodState += new StringBuffer(Tools.bytesToBinaryString(data[55])).reverse().toString();

        StringBuffer data72 = new StringBuffer();
        data72.append(roadInfo).append(roadMotor).append(roadBreak)
                .append(roadSet).append(roadState).append(roadEnable)
                .append(roadTemp).append(foodState);
        String data72Str = data72.toString();

        String roadStateInfo = mDataMap72.get(boxNo);
        try {
            // 状态信息不同时发送
            if (roadStateInfo == null || !roadStateInfo.equals(data72Str)) {
                mDataMap72.put(boxNo, data72Str);
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // 售货机外设及系统状态
    private void tran73(byte[] data) //售货机外设及系统状态
    {
        mlogger.i("73: " + Tools.bytesToHexString(data));
        if (!checkSum(T73_DATA_LEN, data)) {
            return;
        }

        defaultAck(data);

        String runStatus = Tools.bytesToBinaryString(data[4]); // 第8位营业状态，第7位门状态
        // 通过主控数据得到游戏状态
        //getGameState(runStatus);
        String runStatusReserved = Tools.bytesToBinaryString(data[5]); //预留1
        String runStatusReserved2 = Tools.bytesToBinaryString(data[6]); //预留2
        String avmStatus = Tools.bytesToBinaryString(data[7]); // 售货机状态
        String avmStatusReserved = Tools.bytesToBinaryString(data[8]); //预留1
        String avmStatusReserved2 = Tools.bytesToBinaryString(data[9]); //预留2
        String billStatus = Tools.bytesToBinaryString(data[10]); // 纸币器状态
        String billStorageNum = Tools.addLeftZero(String.valueOf(Tools.bytesToInt(Tools.subByte(data, 11, 13))), 5); //纸币存储数量
        String billStoragePrice = Tools.addLeftZero(String.valueOf(Tools.bytesToInt(Tools.subByte(data, 13, 17))), 10); //纸币器存储金额
        String billChangeValue = Tools.addLeftZero(String.valueOf(Tools.bytesToInt(data[17])), 3); //纸币找零面额
        String billChangeNum = Tools.addLeftZero(String.valueOf(Tools.bytesToInt(Tools.subByte(data, 18, 20))), 5); //纸币找零数量
        String billChangePrice = Tools.addLeftZero(String.valueOf(Tools.bytesToInt(Tools.subByte(data, 20, 24))), 10); //纸币找零金额
        String coinStatus = Tools.bytesToBinaryString(data[24]); // 硬币器状态
        //6种面额的硬币存储数量
        String coinStorageNum = "";
        for (int i = 0; i < 6; i++) {
            int s = 25 + i * 2;
            int e = s + 2;
            coinStorageNum += Tools.addLeftZero(String.valueOf(Tools.bytesToInt(Tools.subByte(data, s, e))), 5);
        }
        //6种面额的硬币进数量
        String coinValueNum = "";
        for (int i = 0; i < 6; i++) {
            int s = 37 + i * 2;
            int e = s + 2;
            coinValueNum += Tools.addLeftZero(String.valueOf(Tools.bytesToInt(Tools.subByte(data, s, e))), 5);
        }
        //6种面额的硬币找零数量
        String coinChangeNum = "";
        for (int i = 0; i < 6; i++) {
            int s = 49 + i * 2;
            int e = s + 2;
            coinChangeNum += Tools.addLeftZero(String.valueOf(Tools.bytesToInt(Tools.subByte(data, s, e))), 5);
        }
        //预留52位
        String reserved = "";
        for (int i = 0; i < 52; i++) {
            reserved += "0";
        }

        mData7D = runStatus + avmStatus + billStatus + coinStatus + coinStorageNum.substring(1, 5) + coinStorageNum.substring(6, 10) + billStorageNum.substring(1, 5);

        char door = runStatus.charAt(6);
        if (door == '1') //门开
        {
            if (doorState != DOOR_OPEN) {
                doorState = DOOR_OPEN;
                mlogger.i("door open");
                sendOnDoorStateChangedToListener(true);
//                if(mContext != null) {
//                    //发送门状态广播
//                    Intent intent = new Intent(INTENT_DOOR_STATE);
//                    mContext.sendBroadcast(intent);
//                }
            }
        } else //门关
        {
            if (doorState != DOOR_CLOSE) {
                doorState = DOOR_CLOSE;
                mlogger.i("door close");
                sendOnDoorStateChangedToListener(false);
//                if(mContext != null) {
//                    //发送门状态广播
//                    Intent intent = new Intent(INTENT_DOOR_STATE);
//                    mContext.sendBroadcast(intent);
//                }
            }
        }

        if (Configure.isMonitorOpen) {
            StringBuilder runMessage = new StringBuilder();
            runMessage.append(runStatus).append(runStatusReserved).append(runStatusReserved2)
                    .append(avmStatus).append(avmStatusReserved).append(avmStatusReserved2)
                    .append(billStatus).append("00000000").append("00000000")
                    .append(billStorageNum).append(billStoragePrice).append(billChangeValue).append(billChangeNum).append(billChangePrice)
                    .append(coinStatus).append(coinStorageNum).append(coinValueNum).append(coinChangeNum)
                    .append(reserved).append(Configure.getMacNo());
            String runInfo = runMessage.toString();
            MonitorThread.getInstance().addMessage83(runInfo);
        }
    }

    /*
     * 和校验
     */
    private boolean checkSum(int dataLen, byte[] data) {
        // 可能会执行
        if (data[0] == 0x76 && data.length == 3) {
            dataLen = 3;
        }

        if (dataLen != data.length) {
            mlogger.i("data length[" + data.length + "] not match to the deal length[" + dataLen + "]: " + Tools.bytesToHexString(data));
            return false;
        }
        int checkLen = dataLen - 1;
        byte sumBytes = data[checkLen];
        byte sumByte = 0x00;
        for (int i = 0; i < checkLen; i++) {
            sumByte += data[i];
        }

        if (sumByte != sumBytes) {
            mlogger.i("check plus error:[" + Tools.bytesToHexString(sumByte) + "] accept data :[" + Tools.bytesToHexString(sumBytes) + "] : " + Tools.bytesToHexString(data));
            return false;
        }
        return true;
    }

    //发Ack 响应
    private void defaultAck(byte[] data) {
        SystemClock.sleep(5);
        byte[] afkBytes = new byte[3];
        afkBytes[0] = data[0];
        afkBytes[1] = 0x00;
        afkBytes[2] = data[0];
        mlogger.d("defaultAck(byte[] data) :" + Tools.bytesToHexString(afkBytes));
        try {
            mOutputStream.write(afkBytes);
            mOutputStream.flush();
        } catch (IOException e) {
            mlogger.e("send ack data failed!");
        }
    }

    /**
     * 发送数据
     *
     * @param msg 要发送的数据
     * @return int(0通知成功 - 1通知失败 - 9其他异常 - 暂未分类)
     */
    private int doSendData(byte[] msg) {
        try {
            cmdResult = CMD_NONE;
            // 输出指令
            mlogger.i("76 send: " + Tools.bytesToHexString(msg));
            mOutputStream.write(msg);
            mOutputStream.flush();

            byte[] readBuffer = null;
            int len = 0;
            int temp;
            int tranLen = 0;
//                mBlocked = true;
            while ((temp = mInputStream.read()) != -1) {
//                    mBlocked = false;
//                    mBlockedTime = 0;
                if (len == 0) {
                    tranLen = getTranTypeLen((byte) temp);
                    if (tranLen > 0) {
                        if ((byte) temp == 0x76) {
                            tranLen = 3;
                        }
                        readBuffer = new byte[tranLen];
                        readBuffer[len++] = (byte) temp;
                    } else {
                        break;
                    }
                } else {
                    readBuffer[len++] = (byte) temp;
                }

//                    mBlocked = true;
                int thisLen = mInputStream.read(readBuffer, len, tranLen - len);
//                    mBlocked = false;
//                    mBlockedTime = 0;

                len = thisLen + len;
                if (len == tranLen) {
                    break;
                }
            }
            // if(readBuffer != null)
            //mlogger.i("Receive call back:"+Tools.bytesToHexString(readBuffer));
            if (tranLen > 0) {
                if (len == 3) {
                    // ACK或NAK
                    String rt = Tools.bytesToHexString(readBuffer[1]);
                    mlogger.i("tranlen(rt): " + Tools.bytesToHexString(readBuffer));
                    if ("00".equals(rt)) {
                        return 0;
                    } else if ("15".equals(rt)) {
                        return -1;
                    } else {
                        return -2;
                    }
                } else {
                    // 收到非NAK和ACK
                    readBuffer = Tools.subByte(readBuffer, 0, len);
                    doAnalysis(readBuffer);
                    return -3;
                }
            } else {
                return -9;
            }
        } catch (IOException e) {
            e.printStackTrace();
            return -9;
        }
    }

    /**
     * 设置主控时间
     *
     * @param pTime 14位的时间
     * @return 是否成功
     */
    public boolean setAvmTime(final String pTime) {
        if (pTime == null || pTime.length() != 14) {
            return false;
        }
        byte[] dataBytes = new byte[10];
        dataBytes[0] = 0x76;
        dataBytes[1] = 0x02;
        byte[] timeBytes = Tools.bcdStringToBytes(pTime);
        for (int i = 2; i < dataBytes.length - 1; i++) {
            dataBytes[i] = timeBytes[i - 2];
        }
        dataBytes[9] = getCheckSum(dataBytes, 0, dataBytes.length - 1);
        appendQueueData(dataBytes);
        return true;
    }

    /**
     * 设置系统状态信息
     *
     * @param pBox            货柜号
     * @param pStatus         售卖配置
     * @param pLightType      照明状态 00:自动,01:常闭,02:常亮,不支持:99
     * @param pLightTime      开关灯时间 HHMMHHMM
     * @param pLeftStatus     左室状态（制冷、制热、关闭）00:制冷,01:制热,02:关闭
     * @param pRightStatus    右室状态（制冷、制热、关闭）00:制冷,01:制热,02:关闭
     * @param pGreenLightTime 节能时间 HHMMHHMM
     * @param pColdTemp       制冷温度
     * @param pHotTemp        制热温度
     * @return 指令是否成功下发
     */
    public boolean setAvmStatus(final int pBox, final int pStatus, final String pLightType, final String pLightTime, final String pLeftStatus, final String pRightStatus, final String pGreenLightTime, final int pColdTemp, final int pHotTemp) {
        if (pBox < 1 || pBox > 11) {
            return false;
        }
        byte[] dataBytes = new byte[18];
        try {
            dataBytes[0] = 0x76;
            dataBytes[1] = 0x06;
            dataBytes[2] = (byte) pBox;
            dataBytes[3] = (byte) pStatus;
            dataBytes[4] = (byte) Integer.parseInt(pLightType, 16);
            byte[] bcd = Tools.bcdStringToBytes(pLightTime);
            if (bcd == null || bcd.length != 4) {
                return false;
            }
            System.arraycopy(bcd, 0, dataBytes, 5, 4);
            dataBytes[9] = (byte) Integer.parseInt(pLeftStatus, 16);
            dataBytes[10] = (byte) Integer.parseInt(pRightStatus, 16);
            bcd = Tools.bcdStringToBytes(pGreenLightTime);
            if (bcd == null || bcd.length != 4) {
                return false;
            }
            System.arraycopy(bcd, 0, dataBytes, 11, 4);
            dataBytes[15] = pColdTemp < 0 ? (byte) ((-pColdTemp & 0xFF) & 0x80) : (byte) (pColdTemp & 0xFF);
            dataBytes[16] = pHotTemp < 0 ? (byte) ((-pHotTemp & 0xFF) & 0x80) : (byte) (pHotTemp & 0xFF);
            dataBytes[17] = getCheckSum(dataBytes, 0, dataBytes.length - 1);
            appendQueueData(dataBytes);
        } catch (NumberFormatException e) {
            return false;
        }
        return true;
    }

    /**
     * 请求设置运营的暂停与恢复
     *
     * @param state 0表示恢复运营,1表示运营暂停,3表示单片机重启,4表示主控程序升级(未实现),5表示食品机货道故障全部清除
     * @return 操作是否成功, state参数错误时返回false
     */
    public boolean noticeAvmRunState(final int state) {
        if (state != 0 && state != 1 && state != 3 && state != 4 && state != 5) {
            return false;
        }
        byte[] dataBytes = new byte[5];
        dataBytes[0] = 0x76;
        dataBytes[1] = 0x07;
        dataBytes[2] = (byte) state;
        dataBytes[3] = 0x00; //预留
        dataBytes[4] = 0x00; //CRC
        dataBytes[4] = getCheckSum(dataBytes);
//        mlogger.i("请求设置运营的暂停与恢复: " + Tools.bytesToHexString(dataBytes));
        appendQueueData(dataBytes); //采用队列的方式
        return true;
    }

    /**
     * 设置货柜永久有货
     *
     * @param box 十进制的货柜号，注意只能是食品机的货柜号(即9 8 7 6)，0表示除了饮料机以外其他货柜永久有货
     * @return 操作是否成功, 参数错误时返回false
     */
    public boolean setFGBox(final int box) {
        if (box > 9 || box < 6) {
            if (box != 0) {
                return false;
            }
        }
        byte[] dataBytes = new byte[5];
        dataBytes[0] = 0x76;
        dataBytes[1] = 0x07;
        dataBytes[2] = 0x06; //设置全部货柜全部有货
        dataBytes[3] = (byte) box; //货柜号
        dataBytes[4] = 0x00; //CRC
        dataBytes[4] = getCheckSum(dataBytes);
        appendQueueData(dataBytes); //采用队列的方式
        return true;
    }

    /**
     * 初始化充电状态
     */
    public void resetChargeState() {
        byte[] dataBytes = new byte[5];
        dataBytes[0] = 0x76;
        dataBytes[1] = 0x07;
        dataBytes[2] = 0x09; //设置充电状态
        dataBytes[3] = 0x00;
        dataBytes[4] = 0x00; //CRC
        dataBytes[4] = getCheckSum(dataBytes);
        mChargeState = 0;
        appendQueueData(dataBytes); //采用队列的方式
    }

    /**
     * 设置某一个线路充电状态
     *
     * @param pLine    线路，1-8
     * @param isCharge 是否充电
     * @return 指令是否成功
     */
    public boolean setChargeState(final int pLine, final boolean isCharge) {
        if (pLine < 1 || pLine > 8) {
            return false;
        }
        String charState = Tools.bytesToBinaryString(mChargeState);
        String resultStr = "";
//        for (int i = charState.length() - 1;i >= 0;i--)
//        {
//            char c = charState.charAt(i);
//            if((pLine - 1) == i)
//            {
//                resultStr += isCharge ? '1' : '0';
//            }
//            else
//            {
//                resultStr += c;
//            }
//        }
        for (int i = 0; i < charState.length(); i++) {
            char c = charState.charAt(i);
            if ((charState.length() - pLine) == i) {
                resultStr += isCharge ? '1' : '0';
            } else {
                resultStr += c;
            }
        }
        byte[] chargeBytes = Tools.binaryStringToBytes(resultStr);
        if (chargeBytes == null) {
            return false;
        }
        mChargeState = chargeBytes[0];
        byte[] dataBytes = new byte[5];
        dataBytes[0] = 0x76;
        dataBytes[1] = 0x07;
        dataBytes[2] = 0x09; //设置充电状态
        dataBytes[3] = mChargeState;
        dataBytes[4] = 0x00; //CRC
        dataBytes[4] = getCheckSum(dataBytes);
        appendQueueData(dataBytes); //采用队列的方式
        return true;
    }

    /**
     * 设置货道状态
     *
     * @param box    货柜号
     * @param column 货道号
     * @param mode   食品机料道模式 0表示弹簧，为1表示履带料道
     * @param motor  单双电机 0表示单电机  1表示双电机，可以设置相邻的两个料道合并，可以1合2，不能2合3
     * @param clear  料道故障清除 0表示不清除料道故障  1表示清除料道故障
     * @param set    料道是否设定 0表示设定，1表示取消设定
     * @return 操作是否成功, 参数错误时返回false
     */
    public boolean setColumnState(final int box, final int column, final int mode, final int motor, final int clear, final int set) {
        if (box > 9 || box < 6) {
            return false;
        }
        if (column < 1 || column > 48) {
            return false;
        }
        if (mode < 0 || mode > 1 || motor < 0 || motor > 1 || clear < 0 || clear > 1 || set < 0 || set > 1) {
            return false;
        }
        if (motor == 1 && ((column & 0x01) == 0)) //设置双电机时货道号必须为单数
        {
            return false;
        }
        byte[] dataBytes = new byte[11];
        dataBytes[0] = 0x76;
        dataBytes[1] = 0x09;
        dataBytes[2] = (byte) box; //货柜号
        dataBytes[3] = (byte) column; //货柜号
        dataBytes[4] = (byte) mode; //食品机料道模式 0表示弹簧，为1表示履带料道
        dataBytes[5] = (byte) motor; //单双电机 0表示单电机  1表示双电机，可以设置相邻的两个料道合并，可以1合2，不能2合3
        dataBytes[6] = (byte) clear; //料道故障清除 0表示不清除料道故障  1表示清除料道故障
        dataBytes[7] = (byte) set; //料道是否设定 0表示设定，1表示取消设定
        dataBytes[8] = 0x00; //预留
        dataBytes[9] = 0x00; //预留
        dataBytes[10] = 0x00; //CRC
        dataBytes[10] = getCheckSum(dataBytes);
        appendQueueData(dataBytes); //采用队列的方式
        return true;
    }

    /**
     * 获得校验
     *
     * @param data 要进行校验的数据
     * @return 校验和
     */
    public byte getCheckSum(byte[] data) {
        byte sumByte = 0x00;
        int len = data.length;
        for (int i = 0; i < len; i++) {
            sumByte += data[i];
        }
        return sumByte;
    }

    /**
     * 获得校验
     *
     * @param data   要进行校验的数据
     * @param offset 偏移量
     * @param length 长度
     * @return 校验和
     */
    public byte getCheckSum(byte[] data, final int offset, final int length) {
        byte sumByte = 0x00;
        int len = length;
        for (int i = offset; i < len; i++) {
            sumByte += data[i];
        }
        return sumByte;
    }

    /**
     * 生成加货信息字节数组
     *
     * @param pBox    货柜
     * @param pColumn 货道
     * @param pStock  补货量,99表示永久有货
     * @return 字节数组
     */
    public static byte[] makeAddStockByteArray(final int pBox, final int pColumn, final int pStock) {
        byte[] soldSendData = new byte[8];
        soldSendData[0] = 0x76; //0x76
        soldSendData[1] = 0x05; //设置料道加货信息0x05
        //柜号1字节
        soldSendData[2] = (byte) pBox; //柜号
        //货道号1字节
        soldSendData[3] = (byte) pColumn; //货道号
        //加货数量1字节
        soldSendData[4] = (byte) pStock; //加货数量
        //预留，2字节
        soldSendData[5] = 0x00;
        soldSendData[6] = 0x00;
        //校验和
        byte sumByte = 0x00;
        for (int i2 = 0; i2 < soldSendData.length - 1; i2++) {
            sumByte += soldSendData[i2];
        }
        soldSendData[7] = sumByte;
        return soldSendData;
    }

    /**
     * 生成设置料道信息字节数组
     *
     * @param pBox    货柜
     * @param pColumn 货道
     * @param pPrice  价格，以角为单位
     * @param pCode   商品ID
     * @param isPrize 是否是中奖商品
     * @return 字节数组
     */
    public static byte[] makePriceAndCodeByteArray(final int pBox, final int pColumn, final int pPrice, final int pCode, final boolean isPrize) {
        byte[] soldSendData = new byte[10];
        soldSendData[0] = 0x76; //0x76
        soldSendData[1] = 0x04; //设置料道信息0x04
        //柜号1字节
        soldSendData[2] = (byte) pBox; //柜号
        //货道号1字节
        soldSendData[3] = (byte) pColumn; //货道号
        //商品编号1字节
        soldSendData[4] = (byte) pCode; //商品ID
        //单价2字节
        soldSendData[5] = (byte) ((pPrice >> 8) & 0xFF);
        soldSendData[6] = (byte) (pPrice & 0xFF); //价格
        //抽奖模式1字节
        soldSendData[7] = isPrize ? (byte) 0x01 : (byte) 0x00;
        //预留1字节
        soldSendData[8] = 0x00;
        //校验和
        byte sumByte = 0x00;
        for (int i2 = 0; i2 < soldSendData.length - 1; i2++) {
            sumByte += soldSendData[i2];
        }
        soldSendData[9] = sumByte;
        return soldSendData;
    }

    public void setOnReceiveMoneyChangedListener(final OnReceiveMoneyChangedListener listener) {
        mOnReceiveMoneyChangedListener = listener;
    }

    public void removeOnReceiveMoneyChangedListener() {
        mOnReceiveMoneyChangedListener = null;
    }

    private void sendReceiveMoneyToListener(final int totalMoney, final int paperMoney, final int coinMoney) {
        if (mOnReceiveMoneyChangedListener != null) {
            mOnReceiveMoneyChangedListener.onReceiveMoneyChanged(totalMoney, paperMoney, coinMoney);
        }
    }

    public void setOnGoodsSelectedListener(final OnGoodsSelectedListener listener) {
        mOnGoodsSelectedListener = listener;
    }

    public void removeOnGoodsSelectedListener() {
        mOnGoodsSelectedListener = null;
    }

    private void sendGoodsSelectedToListener(final int box, final int column, final int goodsId, final int price) {
        if (mOnGoodsSelectedListener != null) {
            mOnGoodsSelectedListener.onGoodsSelected(box, column, goodsId, price);
        }
    }

    public void setOnFreshColumnListener(final OnFreshColumnListener listener) {
        mOnFreshColumnListener = listener;
    }

    public void removeOnFreshColumnListener() {
        mOnFreshColumnListener = null;
    }

    private void sendOnFreshColumnToListener() {
        if (mOnFreshColumnListener != null) {
            mOnFreshColumnListener.onFreshColumn();
        }
    }

    public void setOnDoorStateChangedListener(final OnDoorStateChangedListener listener) {
        mOnDoorStateChangedListener = listener;
    }

    public void removeOnDoorStateChangedListener() {
        mOnDoorStateChangedListener = null;
    }

    private void sendOnDoorStateChangedToListener(final boolean isDoorOpen) {
        if (mOnDoorStateChangedListener != null) {
            mOnDoorStateChangedListener.onDoorStateChanged(isDoorOpen);
        }
        //关门之后发送84指令到后台监控
        if (Configure.isMonitorOpen && !isDoorOpen) {
            if (mWarrantyInfo == null) {
                return;
            }
            for (int i = 11; i >= 1; i--) {
                if (mWarrantyInfo.containsKey(i)) {
                    StringBuilder runMessage = new StringBuilder();
                    String[] array = mWarrantyInfo.get(i);
                    for (int j = 0; j < array.length; j++) {
                        if (j > 0) {
                            runMessage.append("|");
                        }
                        runMessage.append(array[j]);
                    }
                    MonitorThread.getInstance().addMessage84(i, runMessage.toString());
                }
            }
        }
    }

    public void setOnGameButtonPressedListener(final OnGameButtonPressedListener listener) {
        mOnGameButtonPressedListener = listener;
    }

    public void removeOnGameButtonPressedListener() {
        mOnGameButtonPressedListener = null;
    }

    private void sendOnGameButtonPressedToListener() {
        if (mOnGameButtonPressedListener != null) {
            mOnGameButtonPressedListener.onGameButtonPressed();
        }
    }

    public void setOnCoinReleaseLeverListener(final OnCoinReleaseLeverListener listener) {
        mOnCoinReleaseLeverListener = listener;
    }

    public void removeOnCoinReleaseLeverListener() {
        mOnCoinReleaseLeverListener = null;
    }

    private void sendOnCoinReleaseLeverToListener() {
        if (mOnCoinReleaseLeverListener != null) {
            mOnCoinReleaseLeverListener.onCoinReleaseLeverPressed();
        }
    }

    public void setOnOutGoodsListener(final OnOutGoodsListener listener) {
        mOnOutGoodsListener = listener;
    }

    public void removeOnOutGoodsListener() {
        mOnOutGoodsListener = null;
    }

    private void sendOnOutGoodsListenerToListener(final int result, final int box, final int column, final int goodsId, final int price, final String tradeTrace, final String machTrace, final String type, final int errorCode) {
        if (mOnOutGoodsListener != null) {
            mOnOutGoodsListener.onOutGoods(result, box, column, goodsId, price, tradeTrace, machTrace, type, errorCode);
        }
    }

    /**
     * 系统配置信息
     *
     * @param box 货柜
     * @return 8售卖配置（后两位有效）+2照明状态+8日光灯时间+2左室状态+2右室状态+8节能时间+2制冷温度（带符号，该温度-4：机器关闭制冷，该温度+2，机器开启制冷）+2左室实际温度（带符号）+2进入加热温度（带符号，该温度+3：机器关闭制热，该温度-3，机器开启制热）+2右室实际温度（带符号）
     */
    public String getAVMConfigInfo(int box) {
        return mDataMap7A.get(box);
    }

    /**
     * 机器运行信息
     *
     * @return 8运行状态（后六位有效）+8售货机状态+8纸币器状态+8硬币器状态+8硬币器检测到的硬币数量（4位0.5元+4位1元）+4纸币数量
     */
    public String getMachRunInfo() {
        return mData7D;
    }

    /**
     * 根据商品ID获取货道信息
     *
     * @param goodsNo 国标码
     * @return null表示无法获取到这个商品，正常情况下返回商品JSON数据，形如
     * {"name":"冰红茶","code":"1","image":"1.png","category":"1","barcode":"","discount":100,"warranty":0,"expiration":0,"box":11,"column":1,"price":100,"num":0,"isBreak":false,"temperature":0}
     * 分别对应
     * 商品名称,商品ID,图片,分类(1-5),条形码,折扣,保质期,快过期时间,货柜号,货道号,价格(分为单位),库存(11货柜表示出货量而非库存),是否故障货道(对于11货柜来说false表示有货 true表示无货),温度状态(0普通 1制冷 2加热)
     */
    public String getRoadInfo(String goodsNo) {
        ArrayList<GoodsModel> goodsList = GoodsConfiguration.getGoodsList();
        Iterator<GoodsModel> iterator = goodsList.iterator();
        while (iterator.hasNext()) {
            GoodsModel model = iterator.next();
//        for (GoodsModel model : Configure.goodsList) {
            if (model.getBarCode().equals(goodsNo)) {
                String goods = getGoodsByBoxAndColumn(model.getBox(), model.getColumn());
                String goodsJson = "{" + goods + "}";
                try {
                    JSONObject object = new JSONObject(goodsJson);
                    int box = 0;
                    int price = 0;
                    int num = 0;
                    boolean isBreak = true;
                    int code;
                    box = object.getInt("box");
                    price = object.getInt("price");
                    num = object.getInt("num");
                    isBreak = object.getBoolean("isBreak");
                    code = object.getInt("code");
                    if (box <= 0 || price <= 0) //商品没有设置
                    {
                        continue;
                    }
                    if (code == COLUMN_DISABLED) //禁用的货道
                    {
                        continue;
                    }

                    if (!isBreak && price > 0 && (box == 11 || num > 0)) {
                        //正常
                    } else {
                        //无货
                        continue;
                    }

                } catch (JSONException e) {
                    continue;
                }
                StringBuffer data = new StringBuffer();
                data.append("{");
                data.append("\"name\":\"").append(model.getName()).append("\",");
                data.append("\"image\":\"").append(model.getImage()).append("\",");
                data.append("\"category\":\"").append(model.getType()).append("\",");
                data.append("\"barcode\":\"").append(model.getBarCode()).append("\",");
                data.append("\"discount\":").append(model.getDiscount()).append(",");
                data.append("\"warranty\":").append(model.getWarranty()).append(",");
                data.append("\"expiration\":").append(model.getExpiration()).append(",");
                data.append(goods);
                data.append("}");
                return data.toString();
            }
        }
        return null;
    }

    /**
     * 根据货柜与货道来查询商品信息
     *
     * @param boxNo  货柜号
     * @param roadNo 货道号
     * @return 商品信息, null表示无法获取到这个商品，正常情况下返回商品JSON数据，形如
     * {"name":"冰红茶","image":"1.png","category":"1","barcode":"","discount":100,"warranty":0,"expiration":0,"box":11,"column":1,"price":100,"num":0,"code":"1","isBreak":false,"temperature":0}
     * 分别对应
     * 商品名称,商品ID,图片,分类(1-5),条形码,折扣,保质期,快过期时间,货柜号,货道号,价格(分为单位),库存(11货柜表示出货量而非库存),是否故障货道(对于11货柜来说false表示有货 true表示无货),温度状态(0普通 1制冷 2加热)
     */
    public String getGoodsInfo(int boxNo, int roadNo) {
        ArrayList<GoodsModel> goodsList = GoodsConfiguration.getGoodsList();
        if (mRoadInfo.containsKey(boxNo)) {
            ColumnModel[] models = mRoadInfo.get(boxNo);
            if (roadNo > 0 && roadNo <= models.length) {
                ColumnModel column = models[roadNo - 1];
                //String goodsId = String.valueOf(column.goodsId);
                Iterator<GoodsModel> iterator = goodsList.iterator();
                while (iterator.hasNext()) {
                    GoodsModel model = iterator.next();
                    if (model.getBox() == boxNo && model.getColumn() == roadNo) {
                        StringBuffer data = new StringBuffer();
                        data.append("{");
                        data.append("\"name\":\"").append(model.getName()).append("\",");
                        data.append("\"image\":\"").append(model.getImage()).append("\",");
                        data.append("\"category\":\"").append(model.getType()).append("\",");
                        data.append("\"barcode\":\"").append(model.getBarCode()).append("\",");
                        data.append("\"discount\":").append(model.getDiscount()).append(",");
                        data.append("\"warranty\":").append(model.getWarranty()).append(",");
                        data.append("\"expiration\":").append(model.getExpiration()).append(",");
                        data.append("\"box\":").append(boxNo).append(",");
                        data.append("\"column\":").append(roadNo).append(",");
                        data.append("\"price\":").append(column.price).append(",");
                        data.append("\"num\":").append(column.num).append(",");
                        data.append("\"code\":\"").append(column.goodsId).append("\",");
                        data.append("\"isBreak\":").append(column.isBreak).append(",");
                        data.append("\"temperature\":").append(column.temperatureState);
                        data.append("}");
                        return data.toString();
                    }
                }
                //mlogger.i("goods:" + goodsId + " not in xml file");
                return null;
            }
        }
        return null;
    }

    /**
     * 根据分类获取商品
     *
     * @param pCategory          分类
     * @param isShowNoStockGoods 是否显示无货的商品
     * @return 商品列表，返回JSON字符串，形如:
     * {"data":[
     * {"name":"冰红茶","image":"1.png","category":"1","barcode":"","discount":100,"warranty":0,"expiration":0,"box":11,"column":1,"price":100,"num":0,"code":"1","isBreak":false,"temperature":0},
     * {"name":"冰绿茶","image":"2.png","category":"2","barcode":"","discount":100,"warranty":0,"expiration":0,"box":11,"column":2,"price":100,"num":0,"code":"2","isBreak":false,"temperature":0}
     * ]}
     */
    public String getGoodsByCategory(final int pCategory, final boolean isShowNoStockGoods) {
        //for test only
//        if(pCategory == 1)
//        {
//            String result = "{\"data\":[";
//            result += "{\"name\":\"统一阿萨姆奶茶\",\"code\":\"1\",\"image\":\"0.png\",\"category\":\"1\",\"barcode\":\"\",\"box\":11,\"column\":1,\"price\":100,\"num\":0,\"isBreak\":false,\"temperature\":0},";
//            result += "{\"name\":\"统一阿萨姆奶茶\",\"code\":\"1\",\"image\":\"0.png\",\"category\":\"1\",\"barcode\":\"\",\"box\":11,\"column\":1,\"price\":100,\"num\":0,\"isBreak\":false,\"temperature\":1},";
//            result += "{\"name\":\"统一阿萨姆奶茶\",\"code\":\"1\",\"image\":\"0.png\",\"category\":\"1\",\"barcode\":\"\",\"box\":11,\"column\":1,\"price\":100,\"num\":0,\"isBreak\":false,\"temperature\":2},";
//            result += "{\"name\":\"统一阿萨姆奶茶\",\"code\":\"1\",\"image\":\"0.png\",\"category\":\"1\",\"barcode\":\"\",\"box\":11,\"column\":1,\"price\":100,\"num\":0,\"isBreak\":true,\"temperature\":0},";
//            result += "{\"name\":\"统一阿萨姆奶茶\",\"code\":\"1\",\"image\":\"0.png\",\"category\":\"1\",\"barcode\":\"\",\"box\":11,\"column\":1,\"price\":100,\"num\":0,\"isBreak\":false,\"temperature\":1},";
//            result += "{\"name\":\"统一阿萨姆奶茶\",\"code\":\"1\",\"image\":\"0.png\",\"category\":\"1\",\"barcode\":\"\",\"box\":11,\"column\":1,\"price\":100,\"num\":0,\"isBreak\":false,\"temperature\":1},";
//            result += "{\"name\":\"统一阿萨姆奶茶\",\"code\":\"1\",\"image\":\"0.png\",\"category\":\"1\",\"barcode\":\"\",\"box\":11,\"column\":1,\"price\":100,\"num\":0,\"isBreak\":false,\"temperature\":1},";
//            result += "{\"name\":\"统一阿萨姆奶茶\",\"code\":\"1\",\"image\":\"0.png\",\"category\":\"1\",\"barcode\":\"\",\"box\":11,\"column\":1,\"price\":100,\"num\":0,\"isBreak\":false,\"temperature\":1},";
//            result += "{\"name\":\"统一阿萨姆奶茶\",\"code\":\"1\",\"image\":\"0.png\",\"category\":\"1\",\"barcode\":\"\",\"box\":11,\"column\":1,\"price\":100,\"num\":0,\"isBreak\":false,\"temperature\":1},";
//            result += "{\"name\":\"统一阿萨姆奶茶\",\"code\":\"1\",\"image\":\"0.png\",\"category\":\"1\",\"barcode\":\"\",\"box\":11,\"column\":1,\"price\":100,\"num\":0,\"isBreak\":false,\"temperature\":0},";
//            result += "{\"name\":\"统一阿萨姆奶茶\",\"code\":\"1\",\"image\":\"0.png\",\"category\":\"1\",\"barcode\":\"\",\"box\":11,\"column\":1,\"price\":100,\"num\":0,\"isBreak\":false,\"temperature\":0},";
//            result += "{\"name\":\"统一阿萨姆奶茶\",\"code\":\"1\",\"image\":\"0.png\",\"category\":\"1\",\"barcode\":\"\",\"box\":11,\"column\":1,\"price\":100,\"num\":0,\"isBreak\":false,\"temperature\":0},";
//            result += "{\"name\":\"统一阿萨姆奶茶\",\"code\":\"1\",\"image\":\"0.png\",\"category\":\"1\",\"barcode\":\"\",\"box\":11,\"column\":1,\"price\":100,\"num\":0,\"isBreak\":false,\"temperature\":0},";
//            result += "{\"name\":\"统一阿萨姆奶茶\",\"code\":\"1\",\"image\":\"0.png\",\"category\":\"1\",\"barcode\":\"\",\"box\":11,\"column\":1,\"price\":100,\"num\":0,\"isBreak\":false,\"temperature\":0},";
//            result += "{\"name\":\"统一阿萨姆奶茶\",\"code\":\"1\",\"image\":\"0.png\",\"category\":\"1\",\"barcode\":\"\",\"box\":11,\"column\":1,\"price\":100,\"num\":0,\"isBreak\":false,\"temperature\":0},";
//            result += "{\"name\":\"统一阿萨姆奶茶\",\"code\":\"1\",\"image\":\"0.png\",\"category\":\"1\",\"barcode\":\"\",\"box\":11,\"column\":1,\"price\":100,\"num\":0,\"isBreak\":false,\"temperature\":0}";
//            result += "]}";
//            return result;
//        }
        String category = String.valueOf(pCategory);
        StringBuffer json = new StringBuffer();
        json.append("{\"data\":[");
        StringBuffer data = new StringBuffer();
        ArrayList<GoodsModel> goodsList = GoodsConfiguration.getGoodsList();
        Iterator<GoodsModel> iterator = goodsList.iterator();
        while (iterator.hasNext()) {
            GoodsModel model = iterator.next();
            if (model.getType().equals(category)) {
                int id;
                try {
                    //找到此商品
                    String goods = getGoodsByBoxAndColumn(model.getBox(), model.getColumn());
                    if (goods == null) {
                        continue;
                    }
                    String goodsJson = "{" + goods + "}";
                    JSONObject object = new JSONObject(goodsJson);
                    int box = 0;
                    int price = 0;
                    int num = 0;
                    boolean isBreak = true;
                    int code = COLUMN_DISABLED;
                    box = object.getInt("box");
                    price = object.getInt("price");
                    num = object.getInt("num");
                    isBreak = object.getBoolean("isBreak");
                    code = object.getInt("code");
                    if (box <= 0) //商品没有设置
                    {
                        continue;
                    }
                    if (code == COLUMN_DISABLED) //禁用的货道
                    {
                        continue;
                    }
                    if (price <= 0) {
                        continue;
                    }
                    if (!isShowNoStockGoods) {
//                        int pos,pos2;
//                        pos = goods.indexOf("\"box\":");
//                        if(pos < 0)
//                        {
//                            continue;
//                        }
//                        pos2 = goods.indexOf(",", pos);
//                        if(pos2 < 0)
//                        {
//                            continue;
//                        }
//                        box = Integer.parseInt(goods.substring(pos + 6, pos2));
//                        pos = goods.indexOf("\"price\":");
//                        if(pos < 0)
//                        {
//                            continue;
//                        }
//                        pos2 = goods.indexOf(",", pos);
//                        if(pos2 < 0)
//                        {
//                            continue;
//                        }
//                        price = Integer.parseInt(goods.substring(pos + 8, pos2));
//                        pos = goods.indexOf("\"num\":");
//                        if(pos < 0)
//                        {
//                            continue;
//                        }
//                        pos2 = goods.indexOf(",", pos);
//                        if(pos2 < 0)
//                        {
//                            continue;
//                        }
//                        num = Integer.parseInt(goods.substring(pos + 6, pos2));
//                        pos = goods.indexOf("\"isBreak\":");
//                        if(pos < 0)
//                        {
//                            continue;
//                        }
//                        pos2 = goods.indexOf(",", pos);
//                        if(pos2 < 0)
//                        {
//                            continue;
//                        }
//                        isBreak = goods.substring(pos + 9, pos2).equals("true");
                        if (!isBreak && price > 0 && (box == 11 || num > 0)) {
                            //正常
                        } else {
                            //无货
                            continue;
                        }
                    }
                    if (data.length() > 0) {
                        data.append(",");
                    }
                    data.append("{");
                    data.append("\"name\":\"").append(model.getName()).append("\",");
                    data.append("\"image\":\"").append(model.getImage()).append("\",");
                    data.append("\"category\":\"").append(model.getType()).append("\",");
                    data.append("\"barcode\":\"").append(model.getBarCode()).append("\",");
                    data.append("\"discount\":").append(model.getDiscount()).append(",");
                    data.append("\"warranty\":").append(model.getWarranty()).append(",");
                    data.append("\"expiration\":").append(model.getExpiration()).append(",");
                    data.append(goods);
                    data.append("}");
                } catch (NumberFormatException e) {
                    mlogger.e("convert id error:" + e.getMessage());
                } catch (JSONException e) {
                    mlogger.e("JSONException:" + e.getMessage());
                }
            }
        }
        json.append(data);
        json.append("]}");
//            mLogger.i(json.toString());
        return json.toString();
    }

    /**
     * 根据商品ID获取相关信息
     * @param pId 商品ID 10进制
     * @return 商品信息类JSON字符串数据，形如: "box":11,"column":1,"price":100,"num":0,"isBreak":false,"temperature":0
     * 分别对应:货柜号,货道号,价格(分为单位),库存(11货柜表示出货量而非库存),是否故障货道(对于11货柜来说false表示有货 true表示无货),温度状态(0普通 1制冷 2加热)
     */
    /*private String getGoodsById(final int pId)
    {
        //mlogger.i("id:" + String.valueOf(pId));
//        Set<Integer> keys = mRoadInfo.keySet();

        int box = 0;
        int road = 0;
        int price = 0;
        int num = 0;
        boolean isBreak = false;
        int temperature = 0;

        //for (Integer key : keys) {
        for(int key = 11;key > 0;key--) {
            if(!mRoadInfo.containsKey(key))
            {
                continue;
            }
            ColumnModel[] models = mRoadInfo.get(key);
            for (int i = 0;i < models.length;i++) {
                ColumnModel column = models[i];
                //mlogger.i("column:" + column);
                if(column.goodsId == pId)
                {
                    //mlogger.i("found id match");
                    box = key;
                    road = i + 1;
                    price = column.price;
                    num = column.num;
                    isBreak = column.isBreak;
                    temperature = column.temperatureState;
                    if(!column.isBreak && column.price > 0 && (key == 11 || column.num > 0))
                    {
                        //mlogger.i("found column match");
                        StringBuffer data = new StringBuffer();
                        data.append("\"box\":").append(box).append(",");
                        data.append("\"column\":").append(road).append(",");
                        data.append("\"price\":").append(price).append(",");
                        data.append("\"num\":").append(num).append(",");
                        data.append("\"isBreak\":").append(isBreak).append(",");
                        data.append("\"temperature\":").append(temperature);
                        return data.toString();
                    }
                    else
                    {
                        if(column.isBreak)
                        {
                            isBreak = true;
                        }
                    }
                }
            }
        }
        StringBuffer data = new StringBuffer();
        data.append("\"box\":").append(box).append(",");
        data.append("\"column\":").append(road).append(",");
        data.append("\"price\":").append(price).append(",");
        data.append("\"num\":").append(num).append(",");
        data.append("\"isBreak\":").append(isBreak).append(",");
        data.append("\"temperature\":").append(temperature);
        return data.toString();
    }
    */

    /**
     * 根据商品货柜货道获取相关信息
     *
     * @param pBox    货柜 10进制
     * @param pColumn 货道 10进制
     * @return 商品信息类JSON字符串数据，形如: "box":11,"column":1,"price":100,"num":0,"code":0,"isBreak":false,"temperature":0
     * 分别对应:货柜号,货道号,价格(分为单位),库存(11货柜表示出货量而非库存),商品ID,是否故障货道(对于11货柜来说false表示有货 true表示无货),温度状态(0普通 1制冷 2加热)
     */
    private String getGoodsByBoxAndColumn(final int pBox, final int pColumn) {
        int box = 0;
        int road = 0;
        int price = 0;
        int num = 0;
        int code = 0;
        boolean isBreak = false;
        int temperature = 0;

        if (mRoadInfo.containsKey(pBox)) {
            ColumnModel[] models = mRoadInfo.get(pBox);
            if (pColumn <= models.length && pColumn > 0) {
                ColumnModel column = models[pColumn - 1];
                box = pBox;
                road = pColumn;
                price = column.price;
                num = column.num;
                code = column.goodsId;
                isBreak = column.isBreak;
                temperature = column.temperatureState;
                if (!column.isBreak && column.price > 0 && (pBox == 11 || column.num > 0)) {
                    StringBuffer data = new StringBuffer();
                    data.append("\"box\":").append(box).append(",");
                    data.append("\"column\":").append(road).append(",");
                    data.append("\"price\":").append(price).append(",");
                    data.append("\"num\":").append(num).append(",");
                    data.append("\"code\":").append(code).append(",");
                    data.append("\"isBreak\":").append(isBreak).append(",");
                    data.append("\"temperature\":").append(temperature);
                    return data.toString();
                } else {
                    if (column.isBreak) {
                        isBreak = true;
                    }
                }
            }
        }
        StringBuffer data = new StringBuffer();
        data.append("\"box\":").append(box).append(",");
        data.append("\"column\":").append(road).append(",");
        data.append("\"price\":").append(price).append(",");
        data.append("\"num\":").append(num).append(",");
        data.append("\"code\":").append(code).append(",");
        data.append("\"isBreak\":").append(isBreak).append(",");
        data.append("\"temperature\":").append(temperature);
        return data.toString();
    }

    /**
     * 获取保质期数据
     *
     * @param pBox    货柜
     * @param pColumn 货道
     * @return 保质期数据
     */
    public String getWarranty(final int pBox, final int pColumn) {
        String result = "";
        if (mWarrantyInfo.containsKey(pBox)) {
            String[] array = mWarrantyInfo.get(pBox);
            if (array != null && pColumn <= array.length) {
                result = array[pColumn - 1];
            }
        }
        return result;
    }

    /**
     * 设置保质期数据
     *
     * @param pBox    货柜
     * @param pColumn 货道
     * @param pData   保质期数据
     */
    public void setWarranty(final int pBox, final int pColumn, final String pData) {
        if (mWarrantyInfo.containsKey(pBox)) {
            String[] array = mWarrantyInfo.get(pBox);
            if (array != null && pColumn <= array.length) {
                array[pColumn - 1] = pData;
            }
        } else {
            String[] array = new String[MAX_ROAD_NUM];
            for (int i = 0; i < array.length; i++) {
                if (i == pColumn - 1) {
                    array[i] = pData;
                } else {
                    array[i] = "";
                }
            }
            mWarrantyInfo.put(pBox, array);
        }
    }

    /**
     * 从文件中读取保质期数据
     */
    public void loadWarrantyData() {
        if (Configure.path == null) {
            return;
        }
        File file = new File(Configure.path + "/data/warranty.txt");
        if (!file.exists()) {
            return;
        }

        ConcurrentHashMap<Integer, String[]> warrantyMap = null;
        FileInputStream fis = null;
        ObjectInputStream ois = null;
        try {
            fis = new FileInputStream(file);
            ois = new ObjectInputStream(fis);
            warrantyMap = (ConcurrentHashMap<Integer, String[]>) ois.readObject();
        } catch (IOException e) {
            mlogger.e("read warranty IOException:" + e.getMessage());
        } catch (ClassNotFoundException e) {
            mlogger.e("read warranty ClassNotFoundException:" + e.getMessage());
        } finally {
            try {
                if (ois != null) {
                    ois.close();
                }
                if (fis != null) {
                    fis.close();
                }
            } catch (IOException e) {
                mlogger.e("close warranty file IOException:" + e.getMessage());
            }
        }
        if (warrantyMap != null) {
            mWarrantyInfo = warrantyMap;
        }
    }

    /**
     * 保存保质期数据到文件中
     */
    public void saveWarrantyData() {
        FileOutputStream fos = null;
        ObjectOutputStream oos = null;
        File file = new File(Configure.path + "/data");
        try {
            if (!file.exists()) {
                file.mkdirs();
            }
            file = new File(Configure.path + "/data/warranty.txt");
            if (!file.exists()) {
                file.createNewFile();
            }
            fos = new FileOutputStream(file);
            oos = new ObjectOutputStream(fos);
            oos.writeObject(mWarrantyInfo);
        } catch (IOException e) {
            mlogger.e("write warranty IOException:" + e.getMessage());
        } finally {
            try {
                if (oos != null) {
                    oos.close();
                }
                if (fos != null) {
                    fos.close();
                }
            } catch (IOException e) {
                mlogger.e("close warranty file IOException:" + e.getMessage());
            }
        }
    }

    /**
     * 通过柜号获取保质期数据
     *
     * @param pBox 柜号
     * @return 保质期数据
     */
    public String getWarrantyFromBox(final int pBox) {
        if (mWarrantyInfo.containsKey(pBox)) {
            StringBuilder runMessage = new StringBuilder();
            String[] array = mWarrantyInfo.get(pBox);
            for (int j = 0; j < array.length; j++) {
                if (j > 0) {
                    runMessage.append("|");
                }
                runMessage.append(array[j]);
            }
            return runMessage.toString();
        }
        return "";
    }

    public void decrementWarranty(final int pBox, final int pColumn) {
        if (mWarrantyInfo.containsKey(pBox)) {
            String[] array = mWarrantyInfo.get(pBox);
            if (pColumn >= array.length) {
                return;
            }
            int pos = pColumn - 1;
            String data = array[pos];
            List<WarrantyModel> modelList = WarrantyModel.string2list(data);
            if (modelList.size() == 0) {
                return;
            }
            WarrantyModel model = modelList.get(0);
            if (model.getStock() == 1) {
                modelList.remove(0);
            } else {
                model.setStock(model.getStock() - 1);
            }
            String result;
            if (modelList.size() == 0) {
                result = "";
            } else {
                result = WarrantyModel.list2string(modelList);
            }
            array[pos] = result;
        }
    }

    /**
     * 触发退币操作
     */
    public void coinRelease() {
        if (totalMoney <= 0) {
            return;
        }
        int tradeTrace = new java.util.Random().nextInt();
        tradeTrace = tradeTrace % 999999 + 1; //随机生成一个流水号
        if (tradeTrace < 0) {
            tradeTrace = -tradeTrace;
        }
        String no = Tools.addLeftZero(String.valueOf(tradeTrace), 6);
        String order = Configure.getMacNo() + Tools.addLeftZero(no, 14); //货柜号为97表示退币，后面参数忽略
        outGoods(97, 1, 65530, OUT_GOODS_CASH, no, order);
    }

    /**
     * 虚拟出货，即只扣款不出货
     *
     * @param price      以分为单位的价格
     * @param type       2位的出货类型
     * @param tradeTrace 6位的数字流水号，需要自己生成
     * @param order      订单号，需要自己生成
     * @return 指令是否成功
     */
    public boolean virtualOut(final int price, final String type, final String tradeTrace, final String order) {
        if (price < 1 || price > 655350)
            return false;
        if (type == null || type.length() != 2)
            return false;
        if (tradeTrace == null || tradeTrace.length() != 6)
            return false;
        //货柜号为99表示虚拟出货
        return outGoods(99, 1, price, type, tradeTrace, order);
    }

    private void loadBox11Stock() {
        for (int i = 0; i < mBox11Stock.length; i++) {
            mBox11Stock[i] = 0;
        }
        File file = new File(Configure.path + "/data/");
        if (!file.exists()) {
            file.mkdirs();
        }
        file = new File(Configure.path + "/data/box11stock.txt");
        if (!file.exists()) //文件不存在
        {
            saveBox11Stock();
        } else {
            FileInputStream fis = null;
            ObjectInputStream ois = null;
            try {
                fis = new FileInputStream(file);
                ois = new ObjectInputStream(fis);
                mBox11Stock = (int[]) ois.readObject();
            } catch (IOException e) {
                mlogger.e("read box11stock IOException:" + e.toString());
            } catch (ClassNotFoundException e) {
                mlogger.e("read box11stock ClassNotFoundException:" + e.toString());
            } finally {
                try {
                    if (ois != null) {
                        ois.close();
                    }
                    if (fis != null) {
                        fis.close();
                    }
                } catch (IOException e) {
                    mlogger.e("close file IOException:" + e.toString());
                }
            }
        }
    }

    public void saveBox11Stock() {
        File file = new File(Configure.path + "/data/box11stock.txt");
        FileOutputStream fos = null;
        ObjectOutputStream oos = null;
        try {
            if (!file.exists()) {
                file.createNewFile();
            }
            fos = new FileOutputStream(file);
            oos = new ObjectOutputStream(fos);
            oos.writeObject(mBox11Stock);
        } catch (IOException e) {
            mlogger.e("write box11stock IOException:" + e.toString());
        } finally {
            try {
                if (oos != null) {
                    oos.close();
                }
                if (fos != null) {
                    fos.close();
                }
            } catch (IOException e) {
                mlogger.e("close file IOException:" + e.toString());
            }
        }
    }

    public void setBox11Stock(final int pColumn, final int pStock) {
        if (pColumn >= MAX_ROAD_NUM) {
            return;
        }
        if (pColumn < 1) {
            return;
        }
        mBox11Stock[pColumn - 1] = pStock;
    }

    public void loadSettingFile() {
        File file = new File(Configure.path + "/data/setting.txt");
        if (!file.exists()) //文件不存在
        {
            saveSettingFile();
        } else {
            FileInputStream fis = null;
            ObjectInputStream ois = null;
            try {
                fis = new FileInputStream(file);
                ois = new ObjectInputStream(fis);
                String data = (String) ois.readObject();
                if (data.length() > 1) {
                    char lock = data.charAt(0);
                    char retry = data.charAt(1);
                    if (lock == '1') {
                        mBoxLock = true;
                    } else {
                        mBoxLock = false;
                    }
                    if (retry == '1') {
                        mBox11Retry = true;
                    } else {
                        mBox11Retry = false;
                    }
                    mlogger.i("setting load lock:" + lock + " retry:" + retry);
                }
            } catch (IOException e) {
                mlogger.e("read setting IOException:" + e.toString());
            } catch (ClassNotFoundException e) {
                mlogger.e("read setting ClassNotFoundException:" + e.toString());
            } finally {
                try {
                    if (ois != null) {
                        ois.close();
                    }
                    if (fis != null) {
                        fis.close();
                    }
                } catch (IOException e) {
                    mlogger.e("close setting file IOException:" + e.toString());
                }
            }
        }
    }

    public void saveSettingFile() {
        File file = new File(Configure.path + "/data/setting.txt");
        FileOutputStream fos = null;
        ObjectOutputStream oos = null;
        try {
            if (!file.exists()) {
                file.createNewFile();
            }
            fos = new FileOutputStream(file);
            oos = new ObjectOutputStream(fos);
            String setting = (mBoxLock ? "1" : "0") + (mBox11Retry ? "1" : "0");
            oos.writeObject(setting);
            mlogger.i("setting load lock:" + (mBoxLock ? "1" : "0") + " retry:" + (mBox11Retry ? "1" : "0"));
        } catch (IOException e) {
            mlogger.e("write setting IOException:" + e.toString());
        } finally {
            try {
                if (oos != null) {
                    oos.close();
                }
                if (fos != null) {
                    fos.close();
                }
            } catch (IOException e) {
                mlogger.e("close setting file IOException:" + e.toString());
            }
        }
    }

    public void addDisabledGoods(final GoodsModel pGoods) {
        for (GoodsModel model : mDisabledGoodsList) {
            if (model.equals(pGoods)) {
                return;
            }
        }
        mDisabledGoodsList.add(pGoods);
    }

    public ArrayList<GoodsModel> getDisabledGoodsList() {
        return mDisabledGoodsList;
    }

    public void removeDisabledGoods(final int pBox, final int pColumn) {
        for (GoodsModel model : mDisabledGoodsList) {
            if (model.getBox() == pBox && model.getColumn() == pColumn) {
                mDisabledGoodsList.remove(model);
                return;
            }
        }
    }

    /**
     * 在关门正常运营的情况下控制电锁门打开
     *
     * @param box 货柜号
     */
    public void setVMCLockOpen(int box) {
        boolean isFood = box <= 9 && box >= 6;
        int offset = 0;
        if (isFood) {
            offset = box - 6;
        } else {
            if (box == 11) {
                offset = 3;
            } else {
                offset = 3 - box;
            }
        }
        byte[] dataBytes = new byte[5];
        dataBytes[0] = 0x76;
        dataBytes[1] = 0x07;
        dataBytes[2] = isFood ? (byte) 0x0B : (byte) 0x0C;
        dataBytes[3] = (byte) (0x01 << (offset * 2));
        dataBytes[4] = getCheckSum(dataBytes, 0, dataBytes.length - 1);

        appendQueueData(dataBytes);
    }

    /**
     * 在关门正常运营的情况下控制电锁门或者取物口打开
     *
     * @param isLock 是否是电锁门，false表示取物口
     */
    public void setVMCLockOpen(boolean isLock) {
        byte[] dataBytes = new byte[5];
        dataBytes[0] = 0x76;
        dataBytes[1] = 0x07;
        dataBytes[2] = isLock ? (byte) 0x0B : (byte) 0x0A;
        dataBytes[3] = 0x40;
        dataBytes[4] = getCheckSum(dataBytes, 0, dataBytes.length - 1);

        appendQueueData(dataBytes);
    }
}
